C语言详解函数与指针的使用

目录
  • 一、函数类型
  • 二、函数指针
  • 三、回调函数
  • 四、小结

一、函数类型

C 语言中的函数有自己特定的类型

函数的类型由返回值,参数类型和参数个数共同决定,如 int add(int i, int j)的类型为 int(int, int)

C 语言中通过 typedef 为函数类型重命名

typedef type name(parameter list)

typedef int f(int, int);

typedef void p(int);

二、函数指针

函数指针用于指向一个函数

函数名是执行函数体的入口地址

可通过函数类型定义函数指针:FuncType* pointer;

也可以直接定义: type (*pointer)(parameter list);

  • pointer 为函数指针变量名
  • type为所指函数的返回值类型
  • parameter list 为所指函数的参数类型列表

下面看一个函数指针使用的代码:

#include <stdio.h>

typedef int(FUNC)(int);

int test(int i)
{
    return i * i;
}

void f()
{
    printf("Call f()...\n");
}

int main()
{
    FUNC* pt = test;
    void(*pf)() = &f;

    printf("pf = %p\n", pf);
    printf("f = %p\n", f);
    printf("&f = %p\n", &f);

    pf();

    (*pf)();

    printf("Function pointer call: %d\n", pt(2));

    return 0;
}
 

输出结果如下:

注意:

1.FUNC* pt = test; 是合法的,test 这个函数名代表的是函数的入口地址,

2.对于函数名来说,取不取地址没有区别,例如上面代码中的 FUNC* pt = test; 也可以写成FUNC* pt = &test;

3.(*pf)(); 相当于 f();

如果我们把void(*pf)() = &f; 改成void(*pf)() = 0x8048400; 直接利用函数指针跳转到 0x8048400 这个地址来执行,结果照样也能正常输出:

面试小问题:如何使用 C 语言直接跳转到某个固定的地址开始执行?

答案:通过函数指针

三、回调函数

回调函数是利用函数指针实现的一种调用机制

回调机制原理

  • 调用者不知道具体事件发生时需要调用的具体函数
  • 被调函数不知道何时被调用,只知道需要完成的任务
  • 当具体事件发生时,调用者通过函数指针调用具体函数

回调机制中的调用者和被调函数互不依赖

下面看一个回调函数的使用示例:

#include <stdio.h>

typedef int(*Weapon)(int);

void fight(Weapon wp, int arg)
{
    int result = 0;

    printf("Fight boss!\n");

    result = wp(arg);

    printf("Boss loss: %d\n", result);
}

int knife(int n)
{
    int ret = 0;
    int i = 0;

    for(i=0; i<n; i++)
    {
        printf("Knife attack: %d\n", 1);
        ret++;
    }

    return ret;
}

int sword(int n)
{
    int ret = 0;
    int i = 0;

    for(i=0; i<n; i++)
    {
        printf("Sword attack: %d\n", 5);
        ret += 5;
    }

    return ret;
}

int gun(int n)
{
    int ret = 0;
    int i = 0;

    for(i=0; i<n; i++)
    {
        printf("Gun attack: %d\n", 10);
        ret += 10;
    }

    return ret;
}

int main()
{
    fight(knife, 3);
    fight(sword, 4);
    fight(gun, 5);

    return 0;
}
 

输出结果如下:

四、小结

  • C 语言中的函数都有特定的类型
  • 可以使用函数类型定义函数指针
  • 函数指针是实现回调机制的关键技术
  • 通过函数指针可以在 C 程序中实现固定地址跳转

到此这篇关于C语言详解函数与指针的使用的文章就介绍到这了,更多相关C语言 函数与指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言的数组指针与函数指针详解

    目录 前言 函数指针语法 数组指针与指针数组 总结 前言 数组指针和函数指针都是C语言比较难的知识点,尤其是函数指针,并且函数指针在开发中有着巨大的作用. 函数指针语法 定义一个函数指针,并通过函数指针间接调用函数: int get_num(int a, int b) { return a + b; } int (*func)(int a, int b); //定义了一个函数指针func,它指向 返回值为int 参数为 int a, int b的函数 func = &get_num; //函数指

  • C语言函数的参数使用指针

    在c语言中实参和形参之间的数据传输是单向的"值传递"方式,也就是实参可以影响形参,而形参不能影响实参.指针变量作为参数也不例外,但是可以改变实参指针变量所指向的变量的值. #include <stdio.h> void swap1(int x,int y),swap2(int *px,int *py),swap3(int *px,int *py); int main(void) { int a=1,b=2; int *pa=&a,*pb=&b; swap1(

  • C语言 从根本上理解指针

    目录 一.* 的意义 二.传值调用与传址调用 三.常量与指针 四.小结 一.* 的意义 在指针声明时,* 号表示所声明的变量为指针 在指针使用时,* 号表示取指针所指向的内存空间中的值 如下: int i = 0; int j = 0; int* p = &i; //指针声明 j = *p; //取值 变量 p 保存着变量 i 的内存地址,即:p <--> &i *p <--> i * 号类似一把钥匙,通过这把钥匙可以打开内存,读取内存中的值. 下面看一个指针的使用

  • C语言函数指针的老生常谈

    目录 函数指针 函数指针的应用 函数指针作为参数实例(qsort函数) 总结 函数指针 本质上是一个指针,只不过指向函数而已. 编译器在编译期间对函数开辟了一块空间,而这快空间的开始地址,就是它的函数指针 . 下面我们也直接用最直观的程序来了解函数指针: #if 1 void func() { printf("hello ptr!"); } int main() { void (*p)(); p = func; p(); } #endif 在这里我们给出了func()函数,并在其中打印

  • C语言返回值指针的函数详解

    #include<stdio.h> void main() { int a[5] = { 1,3,5,7,9 }; int* name[5] = { &a[0],&a[1],&a[2] ,&a[3] ,&a[4] }; int i; for (i = 0; i < 5; i++) { printf("%d ", *name[i]); } printf("\n\n"); } #include<stdio.

  • C语言函数指针详解

    目录 Introduction 函数指针 Function Pointers Exercise 1:qsort中的函数指针 Exercise 2: 总结 Introduction 上一个lab的主要内容为__data pointer__(指向数据的指针)可能在Linux系统中造成的__segmentation fault__.本次lab将考虑__function pointer__(指向函数/代码的指针)可能造成的错误:segfault或其他exceptions. 函数指针 Function P

  • C语言函数指针数组实现计算器功能

    目录 一.概念 二.用途 三.案例:计算器 (1)基础代码编译: (2)使用函数指针数组的实现: 一.概念 数组:一个存放相同类型数据的存储空间. int arr[10]; //数组arr的每个元素是int 指针数组:一个存放指针的数组. int* arr[10]; //数组arr的每个元素是int* 函数指针:一个指向函数的指针,一般用函数名表示. int Add(int x, int y) { return x + y; } int main() { int arr[10] = { 1, 2

  • C语言详解函数与指针的使用

    目录 一.函数类型 二.函数指针 三.回调函数 四.小结 一.函数类型 C 语言中的函数有自己特定的类型 函数的类型由返回值,参数类型和参数个数共同决定,如 int add(int i, int j)的类型为 int(int, int) C 语言中通过 typedef 为函数类型重命名 typedef type name(parameter list) 如 typedef int f(int, int); typedef void p(int); 二.函数指针 函数指针用于指向一个函数 函数名是

  • C语言详解如何应用模拟字符串和内存函数

    目录 1.strlen 求字符串长度 使用案例: 1.计数法 2.不创建临时变量计数器-递归 3.指针-指针的方式 2.长度不受限制的字符串函数 1.strcpy 使用案例: 模拟实现: 2.strcat 使用案例: 模拟实现: 3.strcmp-比较字符串首字母的大小 使用案例: 模拟实现: 3.长度受限制的字符串函数  1.strncpy 使用案例: 2.strncat  使用案例: 3.strncmp 使用案例: 4.strstr-找子串  使用案例: 模拟实现: 5.strtok 用法:

  • C语言详解strcmp函数的分析及实现

    目录 1.函数介绍 1.1.函数接口 1.2.函数分析 1.3.函数的简单使用 1.4.函数使用结果分析 2.库函数strcmp源代码 2.1.库函数源代码 2.2.库函数分析 3.模拟实现 strcmp 函数 3.1.模拟实现 3.2.模拟实现分析 1.函数介绍 1.1.函数接口 int __cdecl strcmp (const char * src,const char * dst); 这里是库函数里面的函数定义接口.这个函数是将 src 和 dst 两个字符串进行比较,即为字符串比较函数

  • C语言详解select函数的使用

    目录 select select API介绍 select 代码 编译运行: select和poll缺点 select select API介绍 主旨思想: 首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中. 调用一个系统函数,监听该列表中的文件描述符,直到这些描述符中的一个或者多个进行I/O操作时,该函数才返回. a. 这个函数是阻塞 b. 函数对文件描述符的检测的操作是由内核完成的 在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作. // sizeof(fd_

  • C语言详解如何实现带头双向循环链表

    目录 创建链表存储结构 创建结点 链表的初始化 双向链表的打印 双向链表尾插 双向链表尾删 双向链表头插 双向链表头删 双向链表查找 双向链表pos前插入结点 双向链表删除pos位置的结点 双向链表的销毁 顺序表和链表的区别 2022042311415360.{C}{C}png" /> 创建链表存储结构 我们需要创建一个结构体来存储一个链表结点的相关信息. typedef int ListDataType;//将ListDataType先定义为int类型,根据需要可以改为不同的类型 //创

  • C语言 详解字符串基础

    目录 一.字符串的概念 二.字符数组与字符串 三.字符串字面量的秘密 四.字符串的长度 五.小结 一.字符串的概念 字符串是有序字符的集合 字符串是程序中的基本元素之一 C 语言中没有字符串的概念 C 语言中通过特殊的字符数组模拟字符串 C 语言中的字符串是以 ‘\0’ 结尾的字符数组 二.字符数组与字符串 在C语言中,双引号引用的单个或多个字符是—种特殊的字面量 存储于程序的全局只读存诸区 本质为字符数组,编译器自动在结尾加上 ‘\0' 字符 下面看一段字符数组与字符串的代码: #includ

  • C语言详解如何实现顺序栈

    目录 顺序栈的定义 顺序栈的理解 准备工作 具体实现 今天说的是关于数据结构顺序栈的一些基本操作c语言实现. 顺序栈的定义 首先,我们先来简单了解一下顺序栈,前面线性表我们知道,根据顺序存储或者链式存储分为顺序表和单链表,同样的,根据存储方式的不同,我们把栈分为顺序存储的栈称为顺序栈,链式存储的栈称为链栈.我们要讲的就是顺序栈.实际上,有了前面线性表的一些知识后,关于栈的操作我们还是比较容易理解的. 顺序栈的理解 问题来了?我们怎么去定义呢?通常我们可以用一个数组和记录栈顶元素位置的变量组成,栈

  • C语言详解无头单向非循环链表各种操作方法

    目录 链表引入 链表介绍 创建链表 打印链表 创建结点 单链表尾插 单链表头插 单链表尾删 单链表头删 在pos位置之前插入数据 在pos位置之后插入数据 删除pos位置结点 删除pos位置之后的结点 销毁链表 链表查找 链表引入 问:上次我们看了顺序表,那么顺序表有些什么优缺点呢? 优点: 顺序表是连续的物理空间,方便下标的随机访问. 缺点: 1.增容需要申请新空间,拷贝数据,释放旧的空间.会有一定消耗. 2.头部或者中间位置插入或者删除数据,需要挪动数据,效率较低. 3.可能存在一定空间占用

  • c语言详解动态内存分配及常见错误的解决

    目录 为什么会有动态内存分配 动态内存函数的介绍 malloc free calloc realloc 常见的错误 对NULL指针的解引用操作 越界访问 对非动态内存进行free 使用free释放动态开辟内存的一部分 对同一块动态内存多次释放 对动态内存内存忘记释放(内存泄漏) 为什么会有动态内存分配 内存使用方式有两种 1.创建一个变量 2.创建一个数组 int a = 1; int arr[10]; 但是这两种方式都有一些特点 1.空间是固定大小的,不会变 2.必须提前知道要开辟多少空间,必

  • C语言详解实现链式二叉树的遍历与相关接口

    目录 前言 一.二叉树的链式结构 二.二叉树的遍历方式 1.1 遍历方式的规则 1.2 前序遍历 1.3 中序遍历 1.4 后序遍历 1.5 层序遍历 三.二叉树的相关接口实现 3.1 二叉树节点个数 3.2 二叉树叶子节点个数 3.3 二叉树第 k 层节点个数 3.4 二叉树的深度(高度) 3.5 二叉树查找值为 x 的节点 3.6 总结 & 注意 四.二叉树的创建和销毁 4.1 通过前序遍历的字符串来构建二叉树 4.2 二叉树销毁 4.3 判断二叉树是否是完全二叉树 前言 二叉树的顺序结构就

随机推荐