事情是這樣的,最近在學習翁老師的Mooc之C語言程序設計,插一句,真的是好老師,講的真的太好了,在c裏的字符串這裏,翁老師演示了一個小程序,大概代碼以下,無非就是想給咱們說明c語言的scanf讀入字符串是遇到空格、Tab和回車爲止,而且會在結尾自動加上表示字符串結束的‘\0’,而後想給咱們演示若是出現字符數組越界會怎樣?!就出現了以下的示例:小程序
#include <stdio.h> int main() { char word[8]; char word2[8]; scanf("%s",word); scanf("%s",word2); printf("%s##%s\n",word,word2); return 0; }
如上就是程序運行結果,有沒有以爲很神奇,反正當時我是震驚了,WoW,太難以想象了,這究竟發生了什麼,老師也留下了這個疑問給咱們,留下咱們苦苦思索,可是百思不得其解,天哪,還好老師給了一個小提示,這和c語言中的數據在計算機中的存放方式有關。數組
因而我開始思考,我仿照翁老師的作法,反正c語言提供了這樣的工具——指針,咱們就輸出地址來看看,我也是知道的,函數在計算機中是經過堆棧的方式來實現的,數據就是經過堆棧來保存,堆棧是很重要的數據結構,其有一個很重要的特性,FILO,後進先出。我增長了輸出字符數組每一個元素的地址的代碼,以下:安全
#include <stdio.h> int main() { char word[8]; char word2[8]; scanf("%s",word); scanf("%s",word2); printf("%s##%s\n",word,word2); printf("%p\n",word); printf("%p\n",word2); printf("\n"); int i; for(i=0;i<8;i++){ printf("%p\n",&word[i]); } printf("\n"); for(i=0;i<8;i++){ printf("%p\n",&word2[i]); } return 0; }
看到了輸出地地址後,加上草圖,一切都簡單明瞭了,總結以下:數據結構
在32位的架構下(我是在32位下編譯的,其實在64位下沒有出現這個狀況,那是由於64位下存儲字大小又不一樣了,其實我還不太解釋的明白,留一個問號???可是經過相同程序的運行,咱們可以發現不一樣,word和word2之間的地址差爲16個字節,當我將字符數組大小擴大爲20時,地址差是按一個存儲字增長的,那64位的存儲字就應該爲16字節,這也確實符合計算機組成裏講的對齊的方式存儲),由於函數是經過堆棧來存儲變量的,是高地址向低地址存儲,咱們能夠把一個字符數組想成一個存儲字,在字內是順序存儲的,就是從低地址向高地址,而存儲字間是則是堆棧的順序,是一種小端存放的方式,這裏,word和word2當作指針是const的,是不變的,當scanf讀入第一個12345678‘\0’字符串的時候是從word所指的位置向高地址依次寫入,雖然越界到了規定的位置之外,可是c語言並無提供有效的機制,即便這是不安全的,可是咱們依然能夠作,能夠順利的寫入,而後讀入第二個12345678‘\0’字符串的時候,一樣的方式,從word2所指向的位置向高地址寫入,很不幸的是,在定義字符數組的時候大小就決定了,而且是連續的分配的內存空間,因此寫入到8的時候,其實就用完了定義的word2的數組大小,可是字符串末尾還有結束字符'\0',雖然是不安全的,可是c語言的編譯器不會發現,所以'\0'被繼續寫到了接下來連續的地址中,就是word所指向的位置,寫入就將原來的1所覆蓋,讀入就ok了,接下來是打印,首先先打印word所指向的字符串,第一個已經從新寫入了,恰好是字符串結束字符,所以什麼也不輸出,而後是打印word2所指向的字符串,打印1.。。。一直要到'\0'纔會認爲是字符串的結束,因此就打印出了12345678‘\0',說到這裏,我也就基本明白了字符串消失之謎的真相,用柯南的話,真相永遠只有一個!架構
哦,原來是這樣,果真不是我不明白,而是我並不知道,計算機的世界真的很精彩,並且頗有意思,等着我,我必定要好好看看。函數