一、通过指针形参在子函数改变常量
大家都知道,C语言子函数的形参,是可以为普通数据类型,也可以为指针的。最初遇到这问题,是在学习STM32的库函数的使用。当初刚接触库函数,对于函数初始化接口,如:
GPIO_Init(GPIOA, &GPIO_InitStructure);
为什么要取初始化结构体变量的地址传递进库函数(&GPIO_InitStructure),而不是直接将结构体变量本身(GPIO_InitStructure)传递进去,不甚了解。直到后来,程序中指针用得多了才有所理解,在这里做个记录。先上代码:
#include <stdio.h>
void AddNum1(int data)
{
data++;
}
void AddNum2(int *data)
{
(*data)++; //自增运算符“++”优先级高于取值符“*”
printf("AddNum2()运行结果:
*data = %d
", *data);
printf(" data = 0x%08X
", data);
printf("&data = 0x%08X
", &data);
}
int main(void)
{
int Num1 = 1;
//测试AddNum1
AddNum1(Num1);
printf("AddNum1()运行结果:
Num1 = %d
", Num1);
Num1 = 1;
printf("
");
//测试AddNum2
AddNum2(&Num1);
printf(" Num1 = %d
", Num1);
printf("&Num1 = 0x%08X
", &Num1);
getchar();
}
运行结果如图:
编译环境为VS2015。
可知,AddNum1没有改变Num1的值,而AddNum2将Num1的值自增了1。分析:
(1)对于子函数形参的理解:
主函数中的代码“AddNum1(Num1);”。实质上,它将Num1的值赋值给了子函数的形参“data”。
可将“AddNum1(Num1);”代码理解为运行了以下代码:
void AddNum1()
{
int data = Num1;
data++;
}
通俗的解释就是,子函数声明了一个整型常量“data”,用“data”缓存“Num1”的值。函数中的其他代码,是针对“data”进行运算的,而“Num1” 除了把它自身的值传递给“data”外没有其他任何操作。所以,“AddNum1();” 这个函数并有没改变“Num1”的值。
(2)指针形参的作用:
我们在对常量,或者是指针进行操作的时候,实质上是对其对应的内存进行操作。对“AddNum2(&Num1);”运行结果以 内存分布图诠释如下:
1、可知,Num1的地址是0x00600FFA0C,”AddNum2(int *data)“;声明了一个指针data,并且将Num1的地址赋值给了指 针 data,相当于执行了”data = 0x00600FFA0C;“,此时”*data“ 等同于”Num1“。
2、接下来的”(*data)++;“,操作的是指针data指向的内存”0x00600FFA0C“,这行代码使这个内存块上存储的常量自 增了1,所以”*data = 2“。由1可知,”*data“ 等同于 ”Num1“,所以“*data = 2 = Num1”。
总结:通过将变量地址传递进子函数,在子函数内操作该地址的内存上存储的数据可达到改变变量的目的。
二、通过指向指针的指针在子函数改变指针的值
这种情况我用得比较少。不过在调用内存管理函数的时候可能会用到。如下代码:
主函数声明了一个指向0x00000001地址的char型指针pMemory,并通过子函数申请内存,将申请得到的地址赋值给pMemory
#include <stdio.h>
#include <stdlib.h>
void GetMemory1(char *pAddr)
{
pAddr = (char *)malloc(sizeof(char) * 100);
}
void GetMemory2(char **pAddr)
{
*pAddr = (char *)malloc(sizeof(char) * 100);
printf(" *pAddr = 0x%08x
", *pAddr);
printf(" pAddr = 0x%08x
", pAddr);
printf(" &pAddr = 0x%08x
", &pAddr);
}
int main(void)
{
char *pMemory = 0x00000001;
printf(" pMemory = 0x%08x
", pMemory);
printf("&pMemory = 0x%08x
", &pMemory);
GetMemory1(pMemory);
printf("/*******GetMemory1();********/
");
printf(" pMemory = 0x%08x
", pMemory);
printf("&pMemory = 0x%08x
", &pMemory);
printf("/*******GetMemory2();********/
");
GetMemory2(&pMemory);
printf(" pMemory = 0x%08x
", pMemory);
printf("&pMemory = 0x%08x
", &pMemory);
getchar();
}
运行结果:
如上,GetMemory1();并不能将地址赋值给pMemory,而GetMemory2();成功将申请得到的地址赋值给pMemory。分析:
(1)GetMemory1();
类似于“AddNum1();”,声明了一个指针pAddr,然后将pMemory的值赋值给pAddr。后续代码改变的是pAddr的数据 而没有改变pMemory。所以没有成功地将申请得到的地址赋值给pMemory。
(2)GetMemory2();
先上图:
进入函数后,pMemory的地址0x008FF718赋值给了pAddr。malloc();申请得到的内存空间的地址0x02BB4D80赋值给了pAddr所指向的内存“*pAddr”(0x008FF718)。又因为*pAddr = pMemory; ,所以申请得到的内存地址成功赋值给了pMemory。
综上所述,当数据被传递进子函数,如需通过子函数改变数据的值,需将它的地址作为形参传递进函数(无论常量亦或是指针)。