contributed by <pjchiou
>
這邊讓我想了很久,後來有去查查哪裡有相關的說明,發現在經典著作內就有專門一小節在說明。
第5.12節 Complicated declarations
共筆當中有一段
在 x86_64 Little-Endian 下執行的結果為
78
56
34
12
這問題很好,可以用好幾週的時間解讀 (算是本學期課程的經典議題)。
參閱 Data alignment and caches 和 Data alignment for speed: myth or reality?,並且設計類似的實驗來驗證 alignment 對資料存取的影響
要注意的是 C 語言只有 call by value (老師上課甚至提到:「根本也不用特別強調,因為就只有這一種。」)以前我看了一些 C++ 的書,甚至分三種
看了有問題的資料,反而讓我在這裡打結。只要記得C 語言只有 call by value 那一切就很自然了。
這段共筆內有一小段程式
我們把它畫成圖會長這個樣子
驗証一下我的想法,我把共筆內的程式小小修改一下,讓 &ptrA 變成一個 LValue,再看看發生了什麼事?程式如下所示:
p 與 ptrptrA 應該存有一樣的值,但是存在不同的位址,輸出如下
ptrptrA=0x7ffc7b947328 stored in 0x7ffc7b947330
p=0x7ffc7b947328 stored in 0x7ffc7b947308
2
接著我用同樣的想法去解析老師在直播內給出的例子
再次強調,永遠只有 call by value ,傳進 function 內的永遠只會是存有相同值的另一個自動變數,搞清楚參數的 type 很重要。
圖中可以輕易看出,原本在 main 中的 ptrA 當然沒有改變。
共筆的開頭:
參考以下程式:
在這個程式中,除了練習使用GDB來驗証共筆的內容外,主要探討變數 a 與 b 的異同處,首先利用GDB設中斷點在第13行,開始觀查這兩個變數的差異。
GDB指令(假設 y 為變數名稱) | a | b |
---|---|---|
whatis y | int [8] | int * |
whatis &y[0] | int * | int * |
whatis y+1 | int * | int * |
whatis &y | int (*)[8] | int ** |
whatis &y+1 | int (*)[8] | int ** |
p y | {0,1,2,3,4,5,6,7} | 0x602010 |
p &y[0] | (int *) 0x7fffffffdc20 | (int *) 0x602010 |
p y+1 | (int *) 0x7fffffffdc24 | (int *) 0x602014 |
p &y | (int (*)[8]) 0x7fffffffdc20 | (int **) 0x7fffffffdc18 |
p &y+1 | (int (*)[8]) 0x7fffffffdc40 | (int **) 0x7fffffffdc20 |
x/8 y | 0x7fffffffdc20: 0 1 2 3 0x7fffffffdc30: 4 5 6 7 |
0x602010: 0 1 2 3 0x602020: 4 5 6 7 |
x/8 &y | 0x7fffffffdc20: 0 1 2 3 0x7fffffffdc30: 4 5 6 7 |
0x7fffffffdc18: 6299664 0 0 1 0x7fffffffdc28: 2 3 4 5 |
從這個結果我們可以得出幾個結論,同時也生出更多問題…
做了這些實驗後,再從頭看一次第5.3節 Pointers and Arrays,當中解釋
才明白這裡在說些什麼,以前只是看過,並沒有真的看懂。
換個角度思考:透過 array subscripting (也就是 []
運算子),我們可存取 a[1]
,那麼可以 (a + 1)[1]
嗎?如果可以,又對應到 a[?] 哪個索引值呢?
Ans: 可以。 因為 (a+1) 是一個 (int *) ,令 int *ptr = a+1; , 則 (a+1)[1] 就相當於 ptr[1] 也就是 *(ptr+1) 同時等於 *(a+2)。
接下來我來驗証,如果做為 parameter 傳入 function ,那麼 a 又會是什麼東西?
GDB指令(假設 y 為變數名稱) | c | d | e |
---|---|---|---|
whatis y | int * | int * | int * |
看到這個,我想實驗已經有結果了,也知道 Linus Torvalds 在氣什麼了。不管用什麼方式丟進 function 都是一樣的東西。 其行為跟第一個實驗中的 b 完全一樣。
一樣用 GDB 來觀察到底哪裡不同。
參考以下程式
結果如下:
GDB指令(假設 y 為變數名稱) | a | b | c |
---|---|---|---|
whatis y | char [12] | char * | char * |
whatis &y | char (*)[12] | char ** | char ** |
p y | "Hello world" | 0x400794 "Hello world" | 0x602010 "Hello world" |
p &y | (char (*)[12]) 0x7fffffffdc3c | (char **) 0x7fffffffdc28 | (char **) 0x7fffffffdc30 |
malloc manpage 寫到 :
The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc(), or realloc().
Otherwise, or if free(ptr) has already been called before, undefined behavior occurs.
如果不是針對malloc系列指令動態分配的記憶體做free(),會觸發 UB Yichung279