一個printf(結(jié)構(gòu)體指針)引發(fā)的血案
一、前言
1. 為什么寫這篇文章
在上周六,我在公眾號里發(fā)了一篇文章:C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹,以直白的語言、一目了然的圖片來解釋指針的底層邏輯,有一位小伙伴對文中的代碼進(jìn)行測試,發(fā)現(xiàn)一個比較奇怪的問題。我把發(fā)來的測試代碼進(jìn)行驗證,思考好久也無法解釋為什么會出現(xiàn)那么奇怪的打印結(jié)果。
為了整理思路,我到陽臺抽根煙。晚上的風(fēng)很大,一根煙我抽了一半,風(fēng)抽了一半,可能風(fēng)也有自己的煩惱。后來一想,煙是我買的,為什么讓風(fēng)來抽?于是我就開始抽風(fēng)!不對,開始回房間繼續(xù)抽代碼,我就不信,這么簡單的 printf 語句,怎么就搞不定?!
于是就有了這篇文章。
2. 你能得到什么收獲函數(shù)參數(shù)的傳遞機(jī)制;可變參數(shù)的實(shí)現(xiàn)原理(va_list);printf 函數(shù)的實(shí)現(xiàn)機(jī)制;面對問題時的分析思路。
友情提醒:文章的前面大部分內(nèi)容都是在記錄思考問題、解決問題的思路,如果你對這個過程不感興趣,可以直接跳到最后面的第四部分,用圖片清晰的解釋了可變參數(shù)的實(shí)現(xiàn)原理,看過一次之后,保管你能深刻記住。
3. 我的測試環(huán)境3.1 操作系統(tǒng)
每個人的電腦環(huán)境都是不一樣的,包括操作系統(tǒng)、編譯器、編譯器的版本,也許任何一個小差別都會導(dǎo)致一些奇奇怪怪的的現(xiàn)象。不過大部分人都是使用 Windows 系統(tǒng)下的 VS 集成開發(fā)環(huán)境,或者 Linux 下的 gcc 命令行窗口來測試。
我一般都是使用 Ubuntu16.04-64 系統(tǒng)來測試代碼,本文中的所有代碼都是在這個平臺上測試的。如果你用 VS 開發(fā)環(huán)境中的 VC 編譯器,可能在某些細(xì)節(jié)上與我的測試結(jié)果又出入,但是問題也不大,遇到問題再分析,畢竟解決問題也是提升自己能力的最快途徑。
3.2 編譯器
我使用的編譯器是 Ubuntu16.04-64 系統(tǒng)自帶的版本,顯示如下:
另外,我安裝的是 64 位系統(tǒng),為了編譯 32 位的可執(zhí)行程序,我在編譯指令中添加了 -m 選項,編譯指令如下:
gcc -m32 main.c -o main
使用 file main 命令來查一下編譯得到的可執(zhí)行文件:
所以,在測試時如果輸出結(jié)果與預(yù)期有一些出入,先檢查一下編譯器。C 語言本質(zhì)上都是一些標(biāo)準(zhǔn),每家的編譯器都是標(biāo)準(zhǔn)的實(shí)現(xiàn)者,只要結(jié)果滿足標(biāo)準(zhǔn)即可,至于實(shí)現(xiàn)的過程、代碼執(zhí)行的效率就各顯神通了。
二、問題導(dǎo)入
1. 網(wǎng)友測試代碼#include <unistd.h>#include <stdio.h>#include <stdlib.h>
typedef struct { int age; char name[8];} Student;
int main(){ Student s[3] = {{1, "a"}, {2, "b"}, {3, "c"}}; Student *p = &(s[0]); printf("%d, %d ", *s, *p);}
2. 期望結(jié)果
根據(jù)上篇文章的討論,我們知道:
s 是一個包含 3 個元素數(shù)組,每個元素的類型是結(jié)構(gòu)體 Student;p 是一個指針,它指向變量s,也就是說指針 p 中保存的是變量 s 的地址,因為數(shù)組名就表示該數(shù)組的首地址。
既然 s 也是一個地址,它也代表了這個數(shù)組中第一個元素的首地址。第一個元素類型是結(jié)構(gòu)體,結(jié)構(gòu)體中第一個變量是 int 型,因此 s 所代表的那個位置是一個 int 型數(shù)據(jù),對應(yīng)到示例代碼中就是數(shù)字 1。因此 printf 語句中希望直接把這個地址處的數(shù)據(jù)當(dāng)做一個 int 型數(shù)據(jù)打印出來,期望的打印結(jié)果是:1, 1。
這樣的分析過程好像是沒有什么問題的。
3. 實(shí)際打印結(jié)果
我們來編譯程序,輸出警告信息:
警告信息說:printf 語句需要 int 型數(shù)據(jù),但是傳遞了一個 Student 結(jié)構(gòu)體類型,我們先不用理會這個警告,因為我們就是想通過指針來訪問這個地址里的數(shù)據(jù)。
執(zhí)行程序,看到實(shí)際打印結(jié)果是:1, 97,很遺憾,與我們的期望不一致!
三、分析問題的思路
1. 打印內(nèi)存模型
可以從打印結(jié)果看,第一個輸出的數(shù)字是 1,與預(yù)期符合;第二個輸出 97,很明顯是字符 'a' 的 ASCII 碼值,但是 p 怎么會指到 name 變量的地址里呢?
首先確認(rèn) 3 個事情:
結(jié)構(gòu)體 Student 占據(jù)的內(nèi)存大小是多少?數(shù)組 s 里的內(nèi)存么模型是怎樣的?s 與 指針變量 p 的值是否正確?
把代碼改為如下:
Student s[3] = {{1, "a"}, {2, "b"}, {3, "c"}};Student *p = s;
printf("sizeof Student = %d ", sizeof(Student));
printf("print each byte in s: ");char *pTmp = p;for (int i = 0; i < 3 * sizeof(Student); i++){ if (0 == i % sizeof(Student)) printf(""); printf("%x ", *(pTmp + i));}printf("");
printf("print value of s and p ");printf("s = 0x%x, p = 0x%x ", s, p);
printf("%d, %d ", *s, *p);
我們先畫一下數(shù)組 s 預(yù)期的內(nèi)存模型,如下:
編譯、測試,打印結(jié)果如下:
從打印結(jié)果看:
結(jié)構(gòu)體 Student 占據(jù) 12 個字節(jié),符合預(yù)期。數(shù)組 s 的內(nèi)存模型也是符合預(yù)期的,一共占據(jù) 36 個字節(jié)。s 與 p 都代表一個地址,打印結(jié)果它倆相同,也是符合預(yù)期的。
那就見鬼了:既然 s 與 p 代表同一個內(nèi)存地址,但是為什么用 *p 讀取 int 型數(shù)據(jù)時,得到的卻是字符 'a' 的值呢?
2. 分開打印信息
既然第一個 *s 打印結(jié)果是正確的,那么就把這個兩個數(shù)據(jù)分開來打印,測試代碼如下:
Student s[3] = {{1, "a"}, {2, "b"}, {3, "c"}};Student *p = s;
printf("%d ", *s);printf("%d ", *p);
請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
-
11月20日火熱報名中>> 2024 智能家居出海論壇
-
11月28日立即報名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會議
-
12月19日立即報名>> 【線下會議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會
-
即日-12.26火熱報名中>> OFweek2024中國智造CIO在線峰會
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書》
-
精彩回顧立即查看>> 【在線會議】多物理場仿真助跑新能源汽車
推薦專題
- 1 從“AI四小龍”看“六小虎”,大模型獨(dú)角獸難破盈利困局
- 2 腦機(jī)接口芯片,華為出了新專利!
- 3 大語言模型會不會成為即將破碎的AI泡沫?
- 4 格創(chuàng)東智入選IDC權(quán)威報告,AI創(chuàng)新成果多領(lǐng)域領(lǐng)先
- 5 高管輪崗機(jī)制,給百度帶來了什么?
- 6 今年諾獎對人工智能的重視,給我們的基礎(chǔ)教育提了個醒
- 7 銀行業(yè)AI大模型,從入局到求變
- 8 巨頭搶布局,VC狂撒錢,為了能讓「AI讀心」這些公司卷瘋了
- 9 阿斯麥ASML:“骨折級”洋相,又成AI第一殺手?
- 10 蘋果市值創(chuàng)新高,iPhone 16能否助力突破4萬億美元大關(guān)?
- 高級軟件工程師 廣東省/深圳市
- 自動化高級工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級銷售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專家 廣東省/江門市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市