一、通過指針形參在子函數改變常量
大家都知道,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()運行結果:\r\n\r\n*data = %d\r\n", *data);
printf(" data = 0x%08X\r\n", data);
printf("&data = 0x%08X\r\n\r\n", &data);
}
int main(void)
{
int Num1 = 1;
//測試AddNum1
AddNum1(Num1);
printf("AddNum1()運行結果:\r\n\r\nNum1 = %d\r\n", Num1);
Num1 = 1;
printf("\r\n\r\n");
//測試AddNum2
AddNum2(&Num1);
printf(" Num1 = %d\r\n", Num1);
printf("&Num1 = 0x%08X\r\n", &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\r\n", *pAddr);
printf(" pAddr = 0x%08x\r\n", pAddr);
printf(" &pAddr = 0x%08x\r\n\r\n", &pAddr);
}
int main(void)
{
char *pMemory = 0x00000001;
printf(" pMemory = 0x%08x\r\n", pMemory);
printf("&pMemory = 0x%08x\r\n\r\n", &pMemory);
GetMemory1(pMemory);
printf("/*******GetMemory1();********/\r\n\r\n");
printf(" pMemory = 0x%08x\r\n", pMemory);
printf("&pMemory = 0x%08x\r\n\r\n", &pMemory);
printf("/*******GetMemory2();********/\r\n\r\n");
GetMemory2(&pMemory);
printf(" pMemory = 0x%08x\r\n", pMemory);
printf("&pMemory = 0x%08x\r\n", &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。
綜上所述,當數據被傳遞進子函數,如需通過子函數改變數據的值,需將它的地址作為形參傳遞進函數(無論常量亦或是指針)。
