數組元素作為函數參數
做一道最常見的題目,問 ary[0] 和 ary[1] 的值是否進行交換?
#include <stdio.h>
#include <stdlib.h>
void swap(int x, int y)
{
int nTmp = 0;
nTmp = x;
x = y;
y = nTmp;
}
int main()
{
int ary[2] = {7, 8};
swap(ary[0], ary[1]);
printf("%08x,%08x\r\n", ary[0], ary[1]);
system("pause");
return 0;
}
大家都知道,調用方的傳參稱為實參,被調方接受的參數稱為形參。形參的值改變,不影響實參的值,是以 ary[0] 和 ary[1] 的值沒有進行交換。但是好多人卻隻會背這句話,并不是真正的了解。
通過函數的設計原則以及機制這篇文章中的知識,分析函數的棧結構,仔細觀察記憶體的分布,來解決這一問題。
按 F10 進入單步調試,從下到上,可以看到橙色部分是 main 函數的參數,綠色部分是 main 函數的傳回位址是,藍色部分是 main 函數調用方的棧底,紫色部分是 main 函數的局部變量,淺藍色是寄存器變量。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLwMGRPVTRE1UeRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1UzNyUTMycTM1IjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
再按 F10 ,位址 0019ff28 寫入了 main 函數的 ary[0] 的值 7 ,位址 0019ff2c 寫入了 main 函數的 ary[1] 的值 8。
main 函數棧的範圍從位址 0019fedc 到位址 0019ff44。esp 的值就是專門指向棧頂的,它的值是位址 0019fedc 。ebp 的值就是專門指向棧底的,他的值是位址 0019ff30。
位址 0019fedc 上面是空閑區。根據函數傳參原則,反向傳參,先傳 ary[1],再傳 ary[0],可以看到,位址0019fed8 寫入了 swap 函數的 y 的值 8 ,位址 0019fed4 寫入了 swap 函數的 x 的值 7,位址004125d3 是 swap 函數的傳回位址。
再按 F10 ,main 函數的棧底被寫入,然後一大片的 cccccccc 作為局部變量,紫色框部分作為寄存器變量。
再按 F10 ,位址 0019fec8 處寫入了 nTmp 的值 0。
再按 F10 ,x 的值等于 ary[0] 等于 7,是以 nTmp = x,位址 0019fec8 寫入了 7。
再按 F10 ,y 的值等于 ary[1] 等于 8,是以 x = y,位址 0019fed4 寫入了 8。
再按 F10 ,nTmp 的值等于 7,是以 y = nTmp,位址 0019fed8 寫入了 7。
函數執行完畢,可以看到 main 函數中位址 0019ff28 和位址 0019ff2c 中的值 ary[0] 和 ary[1],并沒有被修改。
計算機中的傳參是拷貝,不是剪切。傳參之後原記憶體内容不變,複制了一份副本,放在了其他棧空間。棧空間的一切操作,隻針對這個函數所占有的棧空間的記憶體的一切操作,并不影響其他函數棧空間的記憶體中的值的改變。
數組位址作為函數參數
将上面的程式稍作修改,問 ary[0] 和 ary[1] 的值是否進行交換?
#include <stdio.h>
#include <stdlib.h>
void swap(int a[])
{
int nTmp = 0;
nTmp = a[0];
a[0] = a[1];
a[1] = nTmp;
}
int main()
{
int ary[2] = {7, 8};
swap(ary);
printf("%08x,%08x\r\n", ary[0], ary[1]);
system("pause");
return 0;
}
同樣通過調試,深入了解這個程式。
按 F10 進入單步調試,數組名 ary 代表,第 0 個元素的位址常量。觀察框中顯示 ary 的值是 0019ff28。
再按 F10 ,位址 0019ff28 寫入了 ary[0] 的值 7 ,位址 0019ff2c 寫入了 ary[1] 的值 8。
按 F11 進入 swap ,數組 ary 第 0 個元素的位址常量 0019ff28 ,作為實參傳入了 swap 函數的形參記憶體中。
再按 F10 ,swap 函數的局部變量區,位址 0019fecc 寫入了 nTmp 的值 0。
在下一步,單步調試之前,需要說明幾個知識點。
是否影響實參,主要看函數内有沒有對實參的位址進行“間接通路”,有間接通路,就會影響到函數外,沒間接通路,怎麼做都不影響。
[]下标運算可實作間接通路,根據數組這篇文章的知識點,通過下标運算求出位址後,按照數組元素類型去解釋這個位址,就是下标通路。
a 的值是 0019ff28。
a[0] 做下标運算 &a[0] = (int)a + sizeof(int)*0 = 0019ff28,對 位址 0019ff28 解釋為數組元素的類型(整形),就是 7。
a[1] 做下标運算 &a[1] = (int)a + sizeof(int)*1 = 0019ff2c,對 位址 0019ff2c 解釋為數組元素的類型(整形),就是 8。
再按 F10 ,進行單步調試,通過間接通路,位址 0019ff28 的值 7。複制給 nTmp。
再按 F10,通過間接通路,位址 0019ff2c 的值 8 。再通過間接通路,将 8 寫入到 0019ff28 的位址中。
再按 F10,nTmp 的值 7 ,通過間接通路,寫入到位址 0019ff2c 中。
成功實作了 ary[0] 和 ary[1] 的互換。
好多人會以為,傳入的是位址,就會影響到實參,其實隻說對了一半。真正影響到實參,是進行了間接通路。
為了更直覺的在程式中展現,傳入的實參是常量。将第 0 個元素的位址常量 0x0019ff28 直接寫入程式。程式修改為, swap(ary) 換為 swap(0x0019ff28)。
#include <stdio.h>
#include <stdlib.h>
void swap(int a[])
{
int nTmp = 0;
nTmp = a[0];
a[0] = a[1];
a[1] = nTmp;
}
int main()
{
int ary[2] = {7, 8};
swap(0x0019ff28);
printf("%08x,%08x\r\n", ary[0], ary[1]);
system("pause");
return 0;
}
同樣可以,交換 ary[0] 和 ary[1] 的值。
傳入數組 ary 第 0 個元素的位址常量,但不進行間接通路。形參是變量,可以進行 ++,是以 a++。
#include <stdio.h>
#include <stdlib.h>
void swap(int a[])
{
int nTmp = 0;
a++;
}
int main()
{
int ary[2] = {7, 8};
swap(ary);
printf("%08x,%08x\r\n", ary[0], ary[1]);
system("pause");
return 0;
}
就好比,現實中打電話。知道電話号碼,叫出這個人(間接通路),在這個人臉上畫圖案,寫字都可以(間接通路後修改值)。但是,知道電話号碼,在電話号碼上進行加一,不呼叫這個人,那麼對這個人不能造成任何影響。
并沒有改變 ary[0] 和 ary[1] 的值,輸出結果仍為 7, 8。