天天看點

手繪知識點——指針的類型vs指針所指向資料的類型

前天就該寫這篇了,無奈雜事纏身,一拖再拖……此篇部落格并不在計劃以内,隻是看前篇部落格的評論時發現了自身代碼的一個錯誤,就是定義指向變量double型變量b的指針pb的時候,前邊類型寫成了int *,應該是double *,當時不知道是啥情況,這麼明顯的錯誤竟然沒有發現,但是編譯也并沒報錯,主要還是輸出的時候設計到的pb太少,這可能就是天意吧,那麼今天就仔細探究一下這一小塊的知識點吧~

先放上前篇部落格的位址,有需要的朋友可以自查:

https://blog.csdn.net/beyond9305/article/details/101165585

 先說下核心要義吧,我們此時主要探究的就是int和double變量及其指針變量格式化輸出時的一系列騷操作,先截取上篇部落格的部分代碼做個大前提:

int a = 1;
double b=3.1415;
int *pa = &a;
double *pb = &b;
           

這是最基本的普通變量和指針變量定義及初始化(就是這樣我上次也出錯了,好尴尬~),此時我們可以假設四種情況分别進行實驗,看看會出現什麼神奇的結果

1.

printf("*pb的值:%f\n", *pb);
           

pb的類型為double *,所緻資料b的類型為double,格式化輸出為%f,看結果:

手繪知識點——指針的類型vs指針所指向資料的類型

沒啥問題,這裡需要說明一下%f預設輸出6位小數,如果需要更高的精度可以自行設定

2.

printf("*pb的值:%d\n", *pb);
           

依然是輸出pb所指的double類型變量b的值,但格式化輸出控制符為%d,即輸出為整型,看結果:

手繪知識點——指針的類型vs指針所指向資料的類型

這是個啥,亂碼了嗎~我一開始也這麼認為,但大家仔細看看這個值,我們後邊再說

3.

int *pb = &b;
printf("*pb的值:%d\n", *pb);
           

和2相比我依然以整型輸出*pb,但把前邊定義的pb類型改為int *,現在就成了這麼一種情況:讓int *型的指針pb指向double型變量b,并以整型格式化輸出,看結果會是啥樣的:

手繪知識點——指針的類型vs指針所指向資料的類型

和前邊的輸出是一樣的,看來這個數字并非偶然,或許并不是亂碼,而是一個有重要意義的神奇數字

4.

int *pb = &b;
printf("*pb的值:%f\n", *pb);
           

最後一條想必大家也想到了,如上所示:讓int * 型的指針變量pb指向double型變量b,并以浮點型格式化輸出,給大家三秒猜猜結果是個啥,好,時間到~

手繪知識點——指針的類型vs指針所指向資料的類型

這個結果驚不驚喜,意不意外~

咱們的風格是力求精簡,大白話接地氣,先來看一張圖:

手繪知識點——指針的類型vs指針所指向資料的類型

這是3.1415在記憶體中的存儲形式 , 感興趣的朋友可以自行查閱一下浮點型資料在記憶體中的存儲要求 , 這裡簡單說一下就是在C

語言中單精度float和雙精度double在格式化輸出時都可以用%f , 系統會自動将float提升為double , 大家也知道在32及更高版本系統中float占4位元組,而double占8位元組 , 這樣當我們存儲一個double型的變量時就需要64位 , 具體配置設定為 : 符号位(1)+階碼(11)+尾數(52)

于是乎3.1415在記憶體中的表示就如上圖所示 , 這其中涉及到尾數的整數部分的1不算入最終結果 , 階碼移碼的轉換等 , 同志們可重點關注一下這一塊 , 都是基礎知識

我們先來看第二種情況和第三種情況 , 輸出是一樣的,對于前者大家可以這麼了解,我們在記憶體中開辟了連續的八塊空間即八個位元組大小的區域存放double變量b,然後當printf整型格式化輸出時我們隻能擷取低位的4個位元組(可以想象成輸出時格式化控制符為%d就告訴系統我們要輸出的是一個整型資料占據4個位元組 , 而記憶體存放資料的原則是先低後高 , 即先存放低位資料再存放高位資料 , 是以我們得到的4位元組資料是double變量b的低32位資料) , 具體的看下圖吧 :

手繪知識點——指針的類型vs指針所指向資料的類型

為了省事我就沒有把對應的資料填進去~~~

大家可以算一下,從低位開始存放,那麼我們第二種和第三種情況所得到的都是低32位資料,按整型資料去解析的話正好是數字-1065151889(這裡涉及到原碼補碼,如果大家感興趣可以看一下我四年前的文章,感覺講的還算比較詳細:https://blog.csdn.net/beyond9305/article/details/48805919)

至于第四種情況輸出0也不難了解,整型變量指針pb隻知道他指向的記憶體區域為4位元組(即首位址+偏移量,這裡的首位址即pb的值,也就是變量b的位址,而偏移量則是連續的4位元組記憶體區域),但很可惜這片區域存放的并不是一個整型資料,而是部分的浮點型資料,這就很容易混亂,輸出稀奇古怪的結果,那這裡為何會輸出0呢?

由于pb認為自己所控制的區域是一個整型資料,是以按正常輸出的應該是0xXXXXXXXX,我們以十六進制表示比較友善,共八位十六進制數,等價于32位二進制數,但輸出的時候是以64位浮點型格式輸出的,那剩下的32位怎麼補呢,有的朋友會想到那就繼續在記憶體中往高位取32位資料不就得了,但是這樣的話很容易得到奇異值,因為你不知道再往高處走會是存放的啥東西,這一點我們上篇也提到了,如果你先後定義兩個變量,那二者在記憶體中是不一定連續存放的,這是由于系統自身的保護機制,有利于debug時儲存斷點,大概是這意思吧,總是就是不能胡亂往高位取資料,畢竟這不是你的地盤,胡亂取資料很可能造成不必要的麻煩。

那麼怎麼辦呢,最友善的就是在高位添加0了,這樣又能湊夠64位,又不影響資料的大小(當然這樣也會存在個問題,如果我們在前邊加0的話,那對于浮點數來說最高位為符号位,就一直為正數了。。)對應本例就是前邊的0xXXXXXXXX變成了0x00000000XXXXXXXX,大家可以算一下,把這個數按照浮點數的存儲規則,拆分為符号位+階碼+尾數樣式,那麼得到的數是什麼呢,直接說結果吧,是一個小數點後有1000多個0的小數,具體的計算可以參照前邊提到的3.1415的轉換形式,其實這就是一個逆運算而已,是以最後這個小數以十進制的格式輸出後就是大家看到的0.000000,我們前邊也說了,預設的浮點型輸出精度為六位,其真正面目是後邊還有一堆0~~有圖有真相,我們輸出小數點後300位:

手繪知識點——指針的類型vs指針所指向資料的類型

總之大家隻要記住,咱們還是規規矩矩地來,整型和浮點型不要互相穿插,不然會得到意料之外的奇葩結果……

今天的讨論有咩有讓大家想到強制資料類型轉換呢,不過其截取的是高位資料而已,低位的直接丢棄掉了,關于這點感興趣的可以自查哦,了解其底層原理還是很有意思的

大概就是這些内容了吧,可能又啰嗦了,哎,控制不住啊,有些想法一落實到語言上就顯得很無力,各位應該能懂~

最後說個小插曲吧,一開始計算3.1415的二進制時是純手算的:

手繪知識點——指針的類型vs指針所指向資料的類型

然而效果并不好,之後就想到了。。。

手繪知識點——指針的類型vs指針所指向資料的類型

現代科技是多麼地友善……

—如果本篇内容對你有一點點幫助,請點個贊或者收藏關注一下,讓我們一起努力—

——————————————————   分割線   —————————————————————

2019.10.14更新

最近幾篇手繪知識點--指針系列文章閱讀量差異較大,比如第二篇耗費大量精力最終木有過百,心涼啊。。。

剛建立了一個公衆号,一開始的文章是和csdn部落格同步的,後續會着重來做這一塊,比如釋出第一手資訊,抽個獎啥的,希望各位小夥伴支援一下,加個關注,如果能幫忙宣傳一下就更完美了,愛你們,還是那句話,讓我們一起努力,共同進步~

公衆号為“非著名IT表演藝術家”,比較中二的名字,就是靈光一閃,然後這個名字就冒出來了……

大家也可以掃碼關注,拜托了:

手繪知識點——指針的類型vs指針所指向資料的類型