函数指针的几种初始化方法

    <span style="font-family:KaiTi_GB2312;font-size:18px;">
    #include<iostream>
    #include<cstdlib>
    using namespace std;
   
    int func()
    {
    	cout << "my name is tzw" << endl;
    	return 0;
    }
     
    int main()
    {
    	int(*p)() = func;   //函数指针初始化方式1
    	int(*p1)() = &func; //函数指针初始化方式2
     
    	func();//函数调用方式1
    	(*p)();//函数调用方式2
    	p();   //函数调用方式3
     
    	system("pause");
    	return 0;
    }</span>

问题1:初始化函数指针时,为何既可以在函数名之前加&操作符,又可以不加?

问题2:函数调用方式有几种,他们之间的区别是什么

问题3:函数指针的用途有哪些?

1:函数指针初始化方式有两种:方式2中的&操作符是可选因为函数名被使用时总是由编译器把它转化为函数指针,&操作符只是显示的说明了编译器将隐式执行的任务,方式2一般不常用.

2:函数调用方式有三种

函数调用方式1:使用函数名调用函数,执行过程可能和你想象的不一样,函数名首先被转化为一个函数指针,该指针指定函数在内存中的位置,然后,函数调用操作符()调用该函数,执行开始于这个地址的代码。

函数调用方式2:首先对p执行间接访问操作,它把函数指针转化为一个函数名,这个转换并不是真正需要的,因为编译器在执行函数调用操作符之前又会把它转换回去

函数调用方式3:省去了编译器的隐式转化
因此,通过函数指针调用函数时,最好选择方式3

3:函数指针的用途:
1:回调函数:用户把一个函数指针作为参数传递给其他函数,后者将“回调”用户的函数
例如:qsort()标准库快速排序函数;
原型:void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));
参 数:
(待排序数组首地址,数组中待排序元素数量,各元素的占用空间大小,指向函数的指针:用于确定排序的顺序)

2:转移表  :本质:一个函数指针数组

例如:double (*oper_func[])(double, double) = {add, sub, mul, div};  调用:oper_func[i](p1, p2)。

4:函数指针与指针函数的区别:

函数指针:int (*p)() 指针p指向函数,又函数名具有地址概念,初始化:int (*p)() = func; 调用:p()提高运算效率!
指针函数: int *p()   由符号优先级()高于*可知:p()是一个函数,该函数返回值是指针类型!



=
<think> 我们正在讨论C语言结构体中函数指针初始化方法。根据用户的问题,我们需要详细说明如何在结构体中初始化函数指针。参考之前的引用,特别是引用[2]和引用[4]中提到了结构体初始化函数指针的相关内容。 在C语言中,结构体可以包含函数指针作为成员。初始化这样的结构体时,我们需要为函数指针成员赋值,使其指向一个具体的函数。初始化可以在声明结构体变量时进行,也可以在后续的代码中赋值。 以下是几种初始化结构体中函数指针方法: 1. 在声明结构体变量时使用初始化列表(类似于普通结构体变量的初始化): 在初始化列表中,我们可以为函数指针成员指定一个匹配类型的函数地址。 2. 在定义之后,通过赋值语句单独为函数指针成员赋值。 3. 如果结构体变量是通过动态内存分配(如malloc)创建的,那么我们需要在分配内存后,手动为函数指针成员赋值。 注意:函数指针的类型必须与所指向函数的签名(参数类型和返回类型)一致。 下面通过示例代码详细说明: 假设我们有一个结构体`Calculator`,它包含一个函数指针成员`operation`,该函数指针指向一个接受两个`int`参数并返回`int`的函数。 步骤1:定义结构体类型 ```c typedef struct { // 函数指针成员,指向一个函数:接受两个int,返回int int (*operation)(int, int); } Calculator; ``` 步骤2:定义几个符合函数指针类型的函数 ```c int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } ``` 步骤3:初始化结构体变量 方法1:声明时初始化(使用初始化列表) ```c int main() { // 在声明结构体变量时直接初始化函数指针 Calculator calc_add = {add}; // 将operation指向add函数 Calculator calc_sub = {subtract}; // 将operation指向subtract函数 // 使用 printf("10 + 5 = %d\n", calc_add.operation(10, 5)); // 输出:15 printf("10 - 5 = %d\n", calc_sub.operation(10, 5)); // 输出:5 return 0; } ``` 方法2:先声明,后赋值 ```c int main() { Calculator calc; // 将函数指针指向add函数 calc.operation = add; printf("10 + 5 = %d\n", calc.operation(10, 5)); // 15 // 改为指向subtract函数 calc.operation = subtract; printf("10 - 5 = %d\n", calc.operation(10, 5)); // 5 return 0; } ``` 方法3:动态分配结构体并初始化函数指针 ```c int main() { // 动态分配Calculator结构体 Calculator *calc = (Calculator *)malloc(sizeof(Calculator)); if (calc == NULL) { // 错误处理 return -1; } // 初始化函数指针 calc->operation = add; printf("10 + 5 = %d\n", calc->operation(10, 5)); // 15 // 改变指向 calc->operation = subtract; printf("10 - 5 = %d\n", calc->operation(10, 5)); // 5 free(calc); // 释放内存 return 0; } ``` 另外,引用[2]中提到,结构体初始化时可以对成员一一赋值,但要注意顺序。对于函数指针成员,同样可以在初始化列表中赋值。同时,引用[4]中展示了一个结构体包含其他结构体成员以及函数指针的打印,说明函数指针的使用。 注意事项: - 确保函数指针指向的函数签名与函数指针类型匹配。 - 如果结构体包含多个成员(包括函数指针和其他类型),在初始化列表中可以按顺序初始化,也可以使用指定初始化(C99标准): ```c // 假设Calculator结构体还有一个成员int id typedef struct { int id; int (*operation)(int, int); } Calculator; // 指定初始化 Calculator calc_add = { .id = 1, .operation = add }; ``` 总结: 结构体中函数指针初始化方法包括:声明时初始化、后续赋值、动态分配后赋值。关键是给函数指针赋予一个匹配类型的函数地址。 根据用户的问题,我们提供了上述方法。现在,我们生成相关问题: §§相关问题§§ 1. 如何定义指向结构体成员函数的函数指针? 2. C语言中函数指针的类型安全如何保证? 3. 结构体中函数指针与普通成员在内存布局上有何不同? </think> ### C语言结构体中函数指针初始化方法 在C语言中,结构体中的函数指针初始化可以通过以下三种方式实现,这些方法适用于任何函数(不仅是`main`函数): #### 1. **声明时直接初始化(推荐)** 在定义结构体变量时直接为函数指针赋值: ```c #include <stdio.h> // 定义函数指针类型 typedef void (*PrintFunc)(int); // 结构体定义 struct Printer { PrintFunc print; // 函数指针成员 }; // 匹配的函数 void print_number(int num) { printf("Value: %d\n", num); } int main() { // 声明时直接初始化函数指针 struct Printer my_printer = {print_number}; // 直接赋值[^2] my_printer.print(42); // 调用 return 0; } ``` **特点**: - 最简洁安全的方式 - 符合C99/C11标准[^2] - 适用于全局/局部结构体变量 #### 2. **先声明后赋值** 先声明结构体变量,再单独初始化函数指针: ```c int main() { struct Printer my_printer; // 后续单独赋值 my_printer.print = print_number; // 通过赋值语句[^4] my_printer.print(100); return 0; } ``` **适用场景**: - 需要动态切换函数指针时 - 函数指针在条件分支中赋值 #### 3. **动态内存分配初始化** 通过`malloc`动态创建结构体并初始化: ```c struct Printer* create_printer(PrintFunc func) { struct Printer* p = malloc(sizeof(struct Printer)); if (p) p->print = func; // 初始化函数指针[^1] return p; } int main() { struct Printer* dyn_printer = create_printer(print_number); dyn_printer->print(256); free(dyn_printer); return 0; } ``` **注意事项**: - 必须检查`malloc`返回值(避免空指针) - 需手动管理内存释放[^1] - 适合需要长期存在的结构体 --- ### 关键注意事项 1. **类型匹配** 函数指针签名必须与目标函数完全一致: ```c // 错误示例:签名不匹配 void wrong_func(char*); struct Printer p = {wrong_func}; // 编译错误! ``` 2. **未初始化的危险** 未初始化函数指针会导致未定义行为: ```c struct Printer uninit; uninit.print(100); // 程序崩溃![^3] ``` 3. **C99指定初始化(增强可读性)** ```c struct Printer p = { .print = print_number // 明确指定成员[^2] }; ``` 4. **函数指针数组** 结构体可包含函数指针数组: ```c struct Menu { void (*actions[5])(); // 函数指针数组 }; struct Menu m = { {func1, func2} }; // 部分初始化[^4] ``` --- ### 三种方法对比 | 方法 | 优点 | 缺点 | 适用场景 | |---------------------|-----------------------|-----------------------|-----------------------| | 声明时直接初始化 | 安全简洁 | 需在声明时确定函数 | 固定功能的简单结构体 | | 先声明后赋值 | 灵活可动态切换 | 可能遗漏初始化 | 需要运行时变更的场景 | | 动态内存分配 | 生命周期可控 | 需手动管理内存 | 长期存在的复杂对象 | > 最佳实践:优先使用**声明时直接初始化**,需要动态行为时改用**后续赋值法**,涉及复杂生命周期时使用**动态分配**[^1][^2][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值