C語言----指標形參(指向指標的指標形參)
一、通過指標形參在子函式改變常量
大家都知道,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。
綜上所述,當資料被傳遞進子函式,如需通過子函式改變資料的值,需將它的地址作為形參傳遞進函式(無論常量亦或是指標)。