[英]I don't understand C++ pointer arithmetic
我有以下程序,它定義了2個整數和一個指向整數的指針。
#include <stdio.h>
int main() {
int bla=999;
int a=42;
int* pa=&a;
printf("%d \n", *pa);
printf("%d \n", pa);
pa++;
//*pa=666; //runs (no error), but the console is showing nothing at all
printf("%d \n", *pa);
printf("%d \n", pa);
pa++;
//*pa=666; //runs and changes the value of *pa to 666;
printf("%d \n", *pa);
printf("%d \n", pa);
}
輸出是:
42
2686740
2686744
2686744 //這個值很奇怪,我想
999
2686748
地址對我來說很有意義,但第四個值很奇怪,因為它正是int的地址。 有人可以解釋一下這種行為嗎?
當我評論* pa = 666(第一個外觀)時,控制台什么也沒顯示,所以這里有一些錯誤,但是編譯器沒有顯示錯誤。 也許這是因為我的系統上的int的大小,我有一個64位的Windows-os,所以也許int是64位而不是32? 並且因為第二次增量后* pa值是999而不是第一次?
我相信,有很多C程序員可以解釋發生了什么:)
int* pa=&a;
pa
是指向整數的指針,並且定義了訪問*pa
。
一旦遞增指針,指針指向某個內存(在p之后),這個內存未由您分配或您不知道,因此取消引用它會導致未定義的行為。
pa++;
*pa
是UB
編輯:
使用正確的格式說明符打印@haccks指出的指針值%p
您使用錯誤的格式說明符來打印地址。 這將調用未定義的行為,並且一旦調用UB,所有投注都將關閉。 請改用%p
。
printf("%p \n", (void *)pa);
另一個問題是執行pa++;
,您正在訪問未分配的內存和UB的另一個原因。
輸出並不奇怪,可以預料到:你在main()
有三個變量,所有這些變量都存儲在堆棧中,並且它們一個接一個地發生。 其中一個變量是指針本身。 因此,當您取消引用第三行中的指針時,您將獲得指針本身的當前值。
然而,這個輸出是不可預測的,它是未定義的行為:您只能使用指針算法來訪問單個內存對象中的數據,在您的情況下,內存對象只是一個int
。 因此,在第一個pa++
之后訪問*pa
是非法的,並且允許程序從該點開始執行任何操作。
更具體地說,不能保證哪個其他變量遵循某個變量,它們遵循哪個順序,或者是否存在可訪問的存儲器。 甚至在第一個pa++
之后讀取*pa
也會導致程序崩潰。 正如您所見,在許多情況下(這很容易調試)您不會遇到崩潰,但代碼仍然深受打擊。
你並不比你的編譯器更聰明。
正如另一個答案所說,你所做的是未定義的行為。 對於pa
你只是無意義,它與任何可定義的目標的可推理算法都不對應:它是無意義的。
但是我會向你提出一個可能出現的情況。 雖然大部分都可能是錯誤的,因為編譯器會進行優化。
int bla=999;
int a=42;
int* pa=&a;
這些變量在堆棧上分配。
當寫pa = &a
你說“我希望指針pa
等於a
的地址”。
編譯器可能已經在訂單或聲明中分配了內存,這可能會給出類似的結果:
bla
地址為0x00008880 a
地址為0x00008884 pa
地址為0x00008888 當你做pa++
你會說:將我的int指針移動到內存中int的下一個位置。 由於int是32位,你正在做pa = pa + 4bytes
pa = 0x00008888
即pa = 0x00008888
請注意, 偶然! ,你可能指向pa
指針的地址。 所以現在指針pa
包含它自己的地址......這非常深奧,可以稱為ouroboros 。
然后你又要問pa++
...所以pa = pa + 4 bytes
即pa = 0x0000888c
所以現在你可能正在訪問一個未知的內存區域。 它可能是訪問沖突。 如果你想讀或寫,這是未定義的行為。
當您第一次分配指針時,它指向2686740
。 指針是一個整數指針,整數使用4個字節(通常,在你的機器上它使用4個字節)。 這意味着pa++
將把值增加到4,即2686744
。 再做一次導致2686748
如果您要查看生成的匯編代碼,則可以切換局部變量的順序。 代碼運行時,順序是a
, pa
, bla
。 由於您沒有明確控制此排序,因此打印輸出被視為未定義
在你第一次使用pa++
,指針指向自身,這就是為什么你得到了“奇怪的價值”
正如許多其他答案所提到的,這不是指針的好用,應該避免。 在這種情況下,您無法控制指針指向的內容。 更好地使用指針算法將指向數組的開頭,然后執行pa++
指向數組中的下一個元素。 您可能遇到的唯一問題是遞增超過數組的最后一個元素
你們是不是要增加的值a
通過指針*pa
?
如果是這樣,請執行: (*pa)++
。 括號是至關重要的,因為它們意味着“獲取指針的值”,然后使用該地址遞增引用的任何內容。
這與*pa++
完全不同,它只返回*pa
指向的值,然后遞增指針 (而不是它引用的東西)。
C語法的一小陷阱。 K&R有幾頁專門討論這個問題,我建議你嘗試一些例子。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.