2 指针Pointer
2.1 内存、地址和指针
1. 内存Memory
- 计算机内存一般分为栈区、堆区和静态区等;为了有效的使用内存,就把内存划分成一个个小的内存单;
- 可以将内存看成"一排排的房子,每间房子可以容纳一定的数据,并且通过房牌号来区分。"
- 内存通常以1字节(byte)为最小单位;
- 1个字节由8位(bit)组成,位是数据最小存储单;
2. 地址Addresses
- “每个房子都有唯一的房牌号,这个号码就是地址”;
- 内存中的每个字节都有一个唯一的地址,这个地址用于定位和访问内存中的数据。
- 一个地址通常是一个整数值,它表示内存中的位置。例如,一个32位系统中的地址通常是一个32位整数,可以表示2^32个不同的字节位置。用来引用内存中的不同字节,从而读取或写入数据。
3. 指针Pointer
- “指针是每个房子房牌号的别名”;
- 指针变量代表内存中的一个地址;指针就是地址,可以用来访问或操作存储在该地址上的数据。
4. 三者关系
内存、地址和指针的三者关系见下图:
对上图代码解释如下:
int a = 112, b = -1; float c = 3.14; int *d = &a; float *e = &c;
- 指针变量的内容是地址(address);整型指针变量
d
内存存储的是整型变量a
的地址;同理,浮点型指针变量e
存储也是地址,是浮点型变量c
的地址;- 指针变量的值和指针变量指向变量的值不一样;指针存的是所指向变量的地址而不是值。
- 变量(variable)的值(value)是分配给这个变量内存位置所存储的数值;
a
和b
存储整数值,而浮点型c
在内存中也是整数,但是可以被解释为浮点数,关键在于使用这个值的方式;- 编译器实现了"变量名称"和"内存地址"的连接;本质上在硬件层,还是需要通过地址访问内存;
2.2 使用指针
1. 指针变量的声明和初始化
- 指针初始化时,"="的右操作数必须为内存中数据的地址,不可以是变量,也不可以直接用整型地址值;
- (但是
int *p = 0;
除外,该语句表示指针为空)。此时,*p
只是表示定义的是个指针变量,并没有间接取值的意思。
- 正确示例
/*正确的实例1*/ int a = 25; int* ptr = &a; /*正确的实例2*/ int b[10]; int* point = b; // OK 数组名就代表数组的地址 int* p = &b[0]; /*正确的实例3*/ int k; int* p; p = &k; //给p赋值 *p = 7; //给p所指向的内存赋值,即k= 7
- 错误示例
int* p; *p = 7;
- 指针p没有初始化(赋值),其指向的内存位置是随机的,7是一个右值;
- 没有初始化的指针(野指针)。
- 不规范示例
int *a, b, c;//实际上只声明一个指针a //修改 int *a, *b, *c;
2. 指针变量的大小
- 指针变量的大小和系统有关;在32位系统(即x86)中,指针的大小为4字节。在64位系统(即x64)中,指针的大小为8字节。
- 指针有
int*型,char*型,double*型
等不同的类型,但是并不会因为是不同类型的指针,指针所占内存大小发生改变; - 指针变量的大小和指针变量指向内存的空间大小不是一回事;“门牌号决定不了房间的大小”。
输出变量或指针的大小,可以采用如下方法:
printf("%d",sizeof()) //在x86系统下: printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(char)); // 1 字节 char的大小 // 输出结果 4 1
3. 间接访问(解引用)
- 通过一个指针访问它所指向的地址的过程称为间接访问(indirection)或解引用指针(dereferencing the pointer)。
- 使用操作符是单目操作符
*
;
表达式 | 右值 | 类型 |
---|---|---|
a | 112 | int |
b | -1 | int |
c | 3.14 | float |
d | 100 | int * |
e | 108 | float * |
*d | 112 | int |
*e | 3.14 | float |
【注意】
- 若用箭头表示下面的变量的内存指示关系,则在一定程度上存在误解:
- 修正后的如下图所示,“指示关系”是在解引用操作符
*
作用之后才存在;
2.3 const
修饰指针变量
1. 关键字const
的作用
- 声明常量:可以使用
const
声明一个常量;- 防止无意修改:
const
可以用于防止在后续代码中无意间修改一个应该保持不变的值,提高代码的鲁棒性。- 编译器优化:可以使用
const
信息来进行优化,以提高程序的性能,编译器通常不为普通const
常量分配存储空间。
用const
定义常变量的方法很简单,通常在定义变量时前加上const
即可,下面两种写法等价;
const int a = 16; //常用 int const a = 16;
当const
修饰指针时,有两种情况:
- 指针变量本身变成常量;
- 它指向的东西(变量变成常量);
2. 修饰指针的值:const 在 * 右侧 (*const
)
const
放在*
右边:int *const ptr;
- 修饰指针变量,指针是常量;
- 指针的值(指针所指向的地址)没办法修改,但是可以修改它指向实体的值;
int a = 10; int b = 20; int * const pi = &a; /*指针变量p(指向)不可修改*/ pi = &b; //报错 /*指针指向的内容可修改*/ *pi = b;
3. 修饰指针指向内容的值:const 在 * 左侧 (const *
)
const
放在*
左边:const int *ptr;
- 修饰指针指向的值,指针指向的值是常量,没办法修改,但是可以修改指针的值;
int a = 10; int b = 20; const int * pi = &a; /*指针变量p(指向)可修改*/ pi = &b; /*指针指向的内容不可修改*/ *pi = b; //报错
注意:
const int *p; // 指向常量整数的指针 int const *p; // 与上一行等价,也是指向常量整数的指针
4. 二者同时
int const * const p
- 若
*
的左边和右边都被加上了const
,则*p
(指针指向内容的值)和p(指针变量的值,指针的指向)均不能被改变。
int a = 10; int const * const pci = &a;
2.4 指针表达式
即指针变量做"左值"和“右值”。
char ch = “a”; char *cp = &ch;
- 上述声明了两个变量:
ch
和cp
,cp
作为一个指针指向ch
;- 假设
cp
变量地址为100,ch
变量的地址为108,即现在cp
内存的数据为:108,ch
内存的数据为字符a;
分析如下:
序号 | 表达式 | 右值 | 左值 |
---|---|---|---|
1 | &ch |
字符型变量cp的地址信息,即“108”这个值 | 非法 不可寻址,不可作为空间使用 |
2 | cp |
指针变量ch的值,即“108”地址信息 | 可寻址,可作为空间使用 |
3 | &cp |
指针变量cp的地址信息,即“100”这个值 | 非法 不可寻址,不可作为空间使用 |
4 | *cp |
指针变量cp的值,即字符“a”这个值 | 表示ch 的地址空间 |
5 | *cp + 1 |
注意* 优先级高于+ ,先执行* 得到a字符的一个拷贝,a加1,得到字符b |
非法 不可寻址 |
6 | *(cp + 1) |
(cp + 1) 先执行指针加法,得到下一个地址值,*(cp + 1) 解析这个新地址数据内容 |
cp 下一个地址空间 |
7 | ++cp |
先执行+1 操作,指向ch 后面的内存空间;再进行执行拷贝操作,也指向ch 后面的内存空间 |
非法 不可寻址 |
8 | cp++ |
先进行执行拷贝操作,指向ch 原来的内存空间;再执行+1 操作,即指向ch 后面的内存空间 |
非法 不可寻址 |
9 | *++cp |
1. 指向解引用操作 2. 先执行+1 操作,指向ch 后面的内存空间;再进行执行拷贝操作,也指向ch 后面的内存空间; 3. 最终得到ch 后面的内存空间的数据; |
得到ch 后面的内存空间; |
10 | *cp++ |
1. 指向解引用操作 2. 先进行执行拷贝操作,指向ch 原来的内存空间;再执行+1 操作,即指向ch 后面的内存空间; 3. 拷贝操作得到ch 内存空间的数据,+1 得到ch 后面的内存空间的数据; |
拷贝操作得到ch 内存空间,+1 得到ch 后面的内存空间; |
11 | ++*cp |
++ 和* 都是自左向右的结合性,* 先执行 1. 指向解引用操作 2. 先执行+1 操作,指向ch 后面的内存空间;再进行执行拷贝操作,也指向ch 后面的内存空间; 3. 拷贝操作得到ch 内存空间的数据,+1 得到ch 后面的内存空间的数据; |
非法 不可寻址 |
12 | *(cp + 7) |
(cp + 1) 先执行指针加法,得到下一个地址值,*(cp + 7) 解析这个新地址数据内容 |
下一个地址空间 |
【关键】
- 理解指针变量的本质和解引用操作符的本质;
- 理解不同运算符的优先级和结合性;
参考:《C和指针》
2.4 指针和函数
1. 指针做函数的参数
- 典型应用就是:指针变量做形参,操作地址,可以改变实参;
- 形参定义指针
*
,调用传入地址&
;- 与c++的引用用法有区别;
/*正确版本*/ void Swap2(int* px, int* py) {
int temp = 0; temp = *px; *px = *py; *py = temp; } int main() {
int num1 = 12; int num2 = 21; Swap1(num1, num2); printf("Swap1: :num1 = %d num2 = %d\n", num1,num2); Swap2(&num1, &num2); printf("Swap2: :num1 = %d num2 = %d\n",num1, num2); return 0; }
2. 指向函数的指针:函数指针
1. 函数名
函数名是什么?
函数名也是一种特殊的指针,函数名表示着函数的入口地址,标识函数在内存的位置;
具体而言:
- 函数名代表入口地址: 编译系统使用函数名来代表函数的入口地址,这个地址是函数的指针,可以用来访问函数的代码。
- 函数名是指针常量: 函数名本质上是一个函数指针常量,它代表了函数的入口地址,不可修改。
- 函数指针变量: 我们可以定义函数指针变量,这样就用指针变量存储函数的入口地址。这种变量叫做指向函数的指针变量,或函数指针。
- 直接调用和间接调用:
- 使用函数名调用函数是直接调用;
- 使用函数指针变量调用函数是间接调用。
- 函数指针变量使函数使用更加灵活,可以在运行时决定要调用哪个函数。
- 函数指针类型匹配: 函数指针变量只能指向与其函数指针类型匹配的函数。
- 即:函数指针变量和要调用的函数的参数个数、类型、顺序以及返回值类型必须一致。
- 函数指针和普通变量的区别:
- 本质都是地址;
- 普通变量的指针指向内存中的数据区;
- 函数指针实际上是指向内存中的函数指令区,即存储函数执行代码的内存区域。
示例代码如下:
#include<stdio.h> int add_func(int x, int y); int subtract_func(int x, int y); int multiply_func(int x, int y); int main(void) {
int a = 6; int b = 10; /*定义函数指针:有两个整数参数、返回值整数*/ int(*func_ptr)(int, int); /*将func_ptr指向函数add_func*/ func_ptr = add_func; //或 func_ptr = &add_func; printf("add_func address = %p\n", add_func); printf("func_ptr = %p\n", func_ptr); printf("%d + %d = %d\n", a, b, add_func(a, b)); printf("%d + %d = %d\n", a, b, func_ptr(a, b)); /*将func_ptr指向函数subtract_func*/ func_ptr = subtract_func; //或 func_ptr = &subtract_func; printf("subtract_func address = %p\n", subtract_func); printf("func_ptr = %p\n", func_ptr); printf("%d - %d = %d\n", a, b, subtract_func(a, b)); printf("%d - %d = %d\n", a, b, func_ptr(a, b)); /*将func_ptr指向函数multiply_func*/ func_ptr = multiply_func; //或 func_ptr = &multiply_func; printf("multiply_func address = %p\n", multiply_func); printf("func_ptr = %p\n", func_ptr); printf("%d × %d = %d\n", a, b, multiply_func(a, b)); printf("%d × %d = %d\n", a, b, func_ptr(a, b)); return 0; } //定义三个具有相同的形式参数和返回值的函数 int add_func(int x, int y) {
return x + y; } int subtract_func(int x, int y) {
return x - y; } int multiply_func(int x, int y) {
return x * y; }
运行结果:
add_func address = 009D1212 func_ptr = 009D1212 6 + 10 = 16 6 + 10 = 16 subtract_func address = 009D100A func_ptr = 009D100A 6 - 10 = -4 6 - 10 = -4 multiply_func address = 009D1258 func_ptr = 009D1258 6 × 10 = 60 6 × 10 = 60
可以看到:
printf("add_func address = %p\n", add_func);
和printf("func_ptr = %p\n", func_ptr);
的值一样,都是函数的地址;- 通过创建函数指针来存储函数的入口地址和函数地址一样;
printf("%d + %d = %d\n", a, b, add_func(a, b));
和printf("%d + %d = %d\n", a, b, func_ptr(a, b));
的值一样- 通过创建函数指针来调用函数和直接调用原函数结果一样;
- 函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址;
func_ptr = add_func
可以改成func_ptr = &add_func
;
2. 指向函数的指针
- 本质是一个指针变量;又叫函数指针;
- 指向函数的指针包含了函数的地址,可通过它来调用函数;
- 常用在回调函数,一个函数传入另外一个函数的地址,可在某个事件发生时调用特定函数;
- 格式:
函数返回值类型 (* 指针变量名) (函数参数列表)
- 函数返回值类型:该指针变量可以指向什么返回值类型的函数;
- 函数参数列表:该指针变量可以指向什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可;
- 声明中的括号不能省略,省略后是
函数返回值类型 * 指针变量名 (函数参数列表)
,变成一个返回值为指针型的函数;- 为了方便使用:
typedef 函数返回值类型 (* 指针变量名) (函数参数列表);
- “指针变量名fptr”不等于“函数名”;
- 函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址;
fptr = &FuncName;
或者fptr = FuncName;
函数指针怎么使用呢?下面是几种常见方式:
① 通过函数指针调用函数
如上文所述,可以通过函数指针来调用函数。函数指针是一个指针变量,可以做左值使用;
那么,如何通过函数指针去调用函数呢?分为3步:
- 声明函数指针,参数列表不建议省略,两种写法;
- 可以使用:
函数返回值类型 (* 指针变量名) (函数参数列表);
- 也可以使用:
typedef 函数返回值类型 (* 指针变量名) (函数参数列表);
- 可以使用:
- 使指针指向函数,两种写法;
fptr = &FuncName;
- 或者
fptr = FuncName;
- 调用函数,根据声明的函数指针的返回值类型确定调用方式(解释如下面例子所示),也有两种方式。
返回值类型 = (*fptr)(函数参数列表);
,这种方式更好理解;- 或者
返回值类型 = (fptr)(函数参数列表);
举例如下:
现有一个求解两整数最大值的函数,声明如下。怎么定义函数指针来调用这个函数呢?
int Max(int x, int y) //定义Max函数 {
int z; if (x > y) {
z = x; } else {
z = y; } return z; }
根据:函数指针变量和要调用的函数的参数个数、类型、顺序以及返回值类型必须一致,声明函数指针如下:
int Max(int x, int y); //函数声明 /* 1. 直接声明 */ int(*fptr)(int, int); //定义一个函数指针 /* 2. 使用typedef */ typedef int(*FPTR)(int, int); //创建的函数指针别名
使指针指向函数:
int Max(int x, int y); //函数声明 /* 1. 使用直接声明 */ fptr = &Max; //或 fptr = Max; /* 2. 使用typedef */ FPTR MaxPtr = &Max; //或 FPTR MaxPtr = Max;
调用函数:
#include <stdio.h> int Max(int x, int y); //函数声明 typedef int(*FPTR)(int, int); //创建的函数指针别名 int main(void) {
FPTR MaxPtr; //创建函数指针MaxPtr int a, b, c; //把函数Max赋给指针变量MaxPtr, 使MaxPtr指向Max函数 MaxPtr = Max; //输入数值 printf("please enter a and b:"); scanf("%d%d", &a, &b); //通过函数指针来调用其指向的函数,这里是调用Max函数,因为函数的返回值是int,方便输出,这里使用int c变量接收 c = (*MaxPtr)(a, b); //c = (MaxPtr)(a, b); printf("a = %d\nb = %d\nmax = %d\n", a, b, c); return 0; } int Max(int x, int y) //定义Max函数 {
//…… }
② 函数指针作为某个函数的参数
上述解释了如何通过函数指针变量来调用函数,可能这样的代码使用函数指针来调用函数显得过于复杂了。实际上,函数指针有以下优势和特点:
- 动态选择函数,实现多态性: C语言中,使用函数指针允许在运行时选择要调用的函数。当某些函数只有函数名不同,参数列表个数、类型和返回值都相同的情况下,这在需要在不同情况下调用不同函数的情况下非常有用。通过将不同的函数指针赋值给相同的函数指针变量,可以实现不同的行为。例如:排序函数可能有非常多种,如冒泡排序、选择排序、递归排序等等。需要根据实际情况选择调用,但是它们除了函数名不一样,其他都是一样的,这时就可以使用函数指针进行调用。
- 回调函数: 函数指针常用于实现回调函数的机制。在这种情况下,一个函数的地址被传递给另一个函数,以便在适当的时候调用。这是实现事件处理、用户界面和异步编程等的常见模式。
- 提高灵活性: 函数指针增加了代码的灵活性。通过使用函数指针,可以更容易地实现可扩展的、可配置的系统,而无需硬编码特定的函数调用
思考:既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。函数指针作为函数参数的真正的应用还是在回调函数中;
举例如下:
现需要实现两个整数的运算,包括简单的加、减、乘、除和自定义运算(乘方、取余等);
- 各种计算函数定义如下:
/* 加 */ int add_func(int x, int y) {
return x + y; } /* 减 */ int subtract_func(int minuend, int subtrahend) {
return minuend - subtrahend;; } /* 乘 */ int multiply_func(int factor1, int factor2) {
return factor1 * factor2; } /* 除 */ int divide_func(int dividend, int divisor) {
if (divisor != 0) {
return (float)dividend / divisor; //将结果向零取整 } else {
// 返回错误值或进行适当处理 printf("error:divisor can not be 0! \n"); return 0; } } /* 乘方函数 */ int power_func(int base, int exponent) {
int result = 1; for (int i = 0; i < exponent; ++i) {
result *= base; } return result; } /* 取余 */ int modulo_func(int dividend, int divisor) {
if (divisor != 0) {
return dividend % divisor; } else {
// 返回错误值或进行适当处理 printf("error:divisor can not be 0! \n"); return 0; } } //自定义计算函数,参数列表、返回值类型和上述相同 int my_func(int x, int y) {
return 5 * x + 8 * y; }
- 总计算函数
定义一个总计算函数,用来调用上述函数。
//该函数有三个参数,函数指针func_ptr和整数a、b;与add_func,subtract_func,multiply_func等函数形式相同 int processing_func(int (*func_ptr)(int, int), int x, int y) {
/* 通过func_ptr的指针执行传递进来的函数 */ return func_ptr(x, y); } //或者写成 typedef int(*FUNC_PTR)(int, int); int processing_func(FUNC_PTR func_ptr, int x, int y) {
/* 通过func_ptr的指针执行传递进来的函数 */ return (*func_ptr)(x, y); }
- 调用函数
#include<stdio.h> int add_func(int x, int y); int subtract_func(int x, int y); int multiply_func(int x, int y); int modulo_func(int dividend, int divisor); int divide_func(int dividend, int divisor); int power_func(int base, int exponent); int my_func(int x, int y); /* 定义函数指针:有两个整数参数、返回值整数 */ //int(*func_ptr)(int, int); typedef int(*FUNC_PTR)(int, int); //方便使用:定义一个名为FUNC_PTR函数指针类型 //int processing_func(int (*func_ptr)(int, int), int x, int y); int processing_func(FUNC_PTR func_ptr, int x, int y); int main(void) {
int num1 = 6; int num2 = 10; //直接调用 printf("using specific function:\n"); printf("%d + %d = %d\n", num1, num2, add_func(num1, num2)); printf("%d - %d = %d\n", num1, num2, subtract_func(num1, num2)); printf("%d * %d = %d\n", num1, num2, multiply_func(num1, num2)); printf("%d / %d = %d\n", num1, num2, divide_func(num1, num2)); printf("%d raised to the power of %d = %d\n", num1, num2, power_func(num1, num2)); printf("%d modulo %d = %d\n", num1, num2, modulo_func(num1, num2)); printf("5*%d + 8*%d = %d\n", num1, num2, my_func(num1, num2)); printf("\n"); //通过调用compute_func函数计算 printf("using processing_func:\n"); printf("%d + %d = %d\n", num1, num2, processing_func(add_func, num1, num2)); printf("%d - %d = %d\n", num1, num2, processing_func(&subtract_func, num1, num2)); //前文所述:fptr = &FuncName;等价于fptr = FuncName; printf("%d - %d = %d\n", num1, num2, processing_func(subtract_func, num1, num2)); printf("%d * %d = %d\n", num1, num2, processing_func(multiply_func, num1, num2)); printf("%d / %d = %d\n", num1, num2, processing_func(divide_func, num1, num2)); printf("%d raised to the power of %d = %d\n", num1, num2, processing_func(power_func, num1, num2)); printf("%d modulo %d = %d\n", num1, num2, processing_func(modulo_func, num1, num2)); printf("5*%d + 8*%d = %d\n", num1, num2, processing_func(my_func, num1, num2)); return 0; }
运行结果如下:
using specific function: 6 + 10 = 16 6 - 10 = -4 6 * 10 = 60 6 / 10 = 0 6 raised to the power of 10 = 6 modulo 10 = 6 5*6 + 8*10 = 110 using compute_func: 6 + 10 = 16 6 - 10 = -4 6 - 10 = -4 6 * 10 = 60 6 / 10 = 0 6 raised to the power of 10 = 6 modulo 10 = 6 5*6 + 8*10 = 110
3. 回调函数
① 基本概念
回调函数(callback function)是一种特殊的函数,通过函数指针将一个函数传递给另一个函数,从而在某个特定事件发生时执行的机制。回调函数通常用于事件处理、异步编程和处理各种操作系统和框架的API;
- 事件处理:当某个事件发生时(例如按钮被、数据读取完成等),系统调用预先定义好的回调函数来处理相应的逻辑。
- 异步编程:在异步编程中,回调函数可以在异步任务完成时执行,以处理任务的结果或执行其他后续操作。
上述例子的add_func
等各个计算就是一个回调函数。
举例:
需要对一个整数数组进行某种处理,例如将每个素加倍。这时,使用回调函数来定义处理的具体逻辑,使得该处理逻辑可以在主调用的函数中进行使用。
#include <stdio.h> // 定义回调函数类型 typedef void (*ArrayProcessor)(int[], int); // 主调函数,接受回调函数和数据作为参数 void processArray(int array[], int size, ArrayProcessor processor) {
// 执行回调函数 processor(array, size); } // 回调函数1:将数组中的每个素加倍 void doubleArray(int array[], int size) {
for (int i = 0; i < size; ++i) {
array[i] *= 2; } } // 回调函数2:将数组中的每个素平方 void squareArray(int array[], int size) {
for (int i = 0; i < size; ++i) {
array[i] *= array[i]; } } void printArray(int array[], int size) {
printf("Array: "); for (int i = 0; i < size; ++i) {
printf("%d ", array[i]); } printf("\n"); } int main() {
int numbers[] = {
1, 2, 3, 4, 5 }; int size = sizeof(numbers) / sizeof(numbers[0]); // 使用 doubleArray 回调函数,将数组素加倍 processArray(numbers, size, doubleArray); printArray(numbers, size); // 使用 squareArray 回调函数,将数组素平方 processArray(numbers, size, squareArray); printArray(numbers, size); return 0; }
输出结果:
Array: 2 4 6 8 10 Array: 4 16 36 64 100
② 为什么要使用回调函数
回调函数是一种在特定事件发生时由另一个函数调用的函数。使用回调函数的主要目的是实现程序的灵活性和可扩展性。
- 模块化管理:回调函数可以将不同功能的代码分离开来,使代码更加模块化和易于理解。主调函数负责执行基本操作,而回调函数则处理特定的任务。使用者哥调用者之间的代码更加好管理。
- 可扩展性:通过使用回调函数,可以轻松地拓展新的功能,而无需修改主调函数,使得程序更容易扩展和维护。
- 适应不同场景:回调函数允许在不同的情况下采取不同的行动。
4. 指针做函数返回值
- 本质是一个函数;又叫指针函数;
- 格式:
函数类型标识符 *函数名 (参数列表)
;- 函数返回类型是某一类型的指针,返回地址给调用者,通常是指针变量或者地址表达式;
举例:设计一个函数,返回数组素中的最大值指针。
#include <stdio.h> // 指针函数:返回数组中的最大素的指针 int* findMaxInArray(int arr[], int size) {
if (size <= 0) {
return NULL; // 处理数组为空的情况 } int maxIndex = 0; for (int i = 1; i < size; ++i) {
if (arr[i] > arr[maxIndex]) {
maxIndex = i; } } return &arr[maxIndex]; } int main() {
int numbers[] = {
12, 5, 8, 20, 15}; int size = sizeof(numbers) / sizeof(numbers[0]); // 调用指针函数,获取数组中最大素的地址 int* maxPtr = findMaxInArray(numbers, size); // 输出最大素的值 if (maxPtr != NULL) {
printf("The maximum element in the array is: %d\n", *maxPtr); } else {
printf("Array is empty.\n"); } return 0; }
举例:
在嵌入式开发中,使用指针函数来动态分配内存并返回分配的内存地址,以便在程序中处理各种数据结构,例如链表或缓冲区。
#include <stdlib.h> #include<stdio.h> // 定义结构体 typedef struct SensorData {
int sensorId; float value; }*SensorData; // 指针函数:动态分配内存并返回结构体指针 SensorData createSensorData(int id, float val) {
//struct SensorData* sensorDataPtr = (struct SensorData*)malloc(sizeof(struct SensorData)); SensorData sensorDataPtr = (SensorData)malloc(sizeof(struct SensorData)); if (sensorDataPtr != NULL) {
sensorDataPtr->sensorId = id; sensorDataPtr->value = val; } return sensorDataPtr; } int main() {
// 调用指针函数,获取动态分配的结构体指针 SensorData sensor1 = createSensorData(1, 23.5); // 检查指针是否有效 if (sensor1 != NULL) {
// 使用结构体指针访问数据 printf("Sensor ID: %d\n", sensor1->sensorId); printf("Sensor Value: %.2f\n", sensor1->value); // 释放动态分配的内存 free(sensor1); } else {
printf("Failed to allocate memory for sensor data.\n"); } return 0; }
注意:
合理使用指针函数: 指针函数可以在某些情况下提供更灵活的返回选项,但也需要谨慎使用,并避免返回指向无效或已释放内存的指针。
如果指针函数返回的是指向局部变量的指针,应避免在函数外部使用这个指针,因为局部变量的生命周期在函数调用结束时结束。
参考:
- 《C和指针》和《C Primer Plus》
- 【精选】C语言回调函数详解(全网最全)-CSDN博客
- 【C语言进阶】⑥函数指针详解-CSDN博客
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/97650.html