回覆列表
  • 1 # IT劉小虎

    題主的疑問應該是,C語言的指標變數裡存放的是地址,而地址明明是一個整數,為什麼整數還要區分型別?至於指標的加法,題主應該是注意到了有時候 1+1 並不等於 2,對不?其實這並沒有什麼難的,請繼續往下看。

    為何要有不同型別的指標變數

    稍稍思考一下,應該能夠發現,C語言中的指標是透過修改記憶體來修改變數的值的。

    既然指標是透過修改記憶體來修改變數的值的,那麼,一個指標一次修改多少記憶體呢?這就涉及到指標的型別了。請看例子,相關C語言程式碼如下:

    對於陣列 i[8],i 其實就表示這個陣列的首地址,所以可以直接把它賦值給指標變數 p1。這樣一來,我們就可以透過 p1 來修改陣列 i 了。

    指標 p1 是 signed char* 型別的,透過 p1 修改 i 所在記憶體時,一次修改 sizeof(signed char) 位元組,也即 1 位元組。那麼,p1+1 指向的就是 i 的第二個元素(i[1]),執行 * (p1+1) = 5; 以後,i[1] 就等於 5 了。

    按照這個邏輯,p2 是 int* 型別的指標變數,請看上圖,透過 p2 訪問陣列 i 時,一次訪問的實際上是 sizeof(int) = 4 位元組記憶體。所以 * (p2+1) = 9; 實際上修改的是 i 的第 5~8 位元組。

    我們把C語言程式碼寫完整些,透過 p1 修改後,把 i 全部打印出來;透過 p2 修改後,再把 i 全部打印出來,請看如下C語言程式碼:

    編譯並執行這段C語言程式碼,得到如下輸出:

    1 5 3 4 5 6 7 8 1 5 3 4 9 0 0 0 指標的加法

    看到這裡,你可能會有疑問了,i 的地址為 4000,那 p1 和 p2 指向的也是 4000,p1+1 指向 4001 地址,這沒什麼好說的。但是 p2+1 指向的卻是 4004? 4000+1 等於 4004 ,這不是扯淡嗎?!

    這還真不是扯淡,題主可以看看我之前關於C語言資料型別的問答或者文章,應該能夠注意:“+”運算子要求兩邊的運算元是同一型別的,如果不同則會自動轉換。

    p1 和 p2 是指標型別的,而 “+1” 的這個“1”是整型的,因此在做加法之前,會有自動資料型別轉換的過程。p1 是 signed char* 型的指標變數,所以“+1”就相當於“+1 x sizeof(signed char)”,因此 p1+1 = 4001。類似的,p2 加上整型 1 就相當於“+ 1 x sizeof(int)”,因此 p2+1=4004。

    為了驗證我們的分析,下面寫C語言程式碼做實驗,我們分別定義 signed char* 型的指標變數 p1 和 int* 型的指標變數 p2,均賦值為 1,然後分別對 p1 和 p2 加一,列印它們原來的值,和加一後的值,請看如下C語言程式碼:

    編譯並執行這段C語言程式碼,得到如下輸出:

    p1=0x1, p1+1=0x2p2=0x1, p2+1=0x5

    這就驗證了我們的分析。類似的,讀者可以自行分析 long* 、float* 、double* 等任意型別的指標變數的加法運算。

    可以將指標的加數“1”看作有“單位”的,單位大小取決於指標的型別。這樣就好理解 “1+1”不等於 2 的情況了,因為 1千克 + 1毫克 不等於 2 千克,對不?

    指標變數佔多少記憶體空間?

    既然指標變數儲存的是記憶體地址,那麼指標變數的位寬就應該保證能夠儲存最大的地址。例如在大多數 32 位計算機中,指標變數的位寬為 4 位元組,因為多數情況下,在 32 位計算機中,最大的記憶體地址為 0xffffffff,至少需要 4 位元組才能完整儲存。相應的,在大多數 64 位計算機中,指標變數的位寬為 8 位元組。

    指標這麼強,能操作任意地址碼?

    很多程式設計師都說,某型別的變數,一定不能用其他型別的指標操作。這句話其實並不嚴謹,例如上面舉的例子中的 char 型別陣列 i[8],我們完全可以使用 int* p2 指標把它當做兩個 int 型變數使用。

    只不過一定要小心 p2 別超過 i[8] 的範圍了,p2+2 指向的就是陣列 i 後的地址了。這裡可能儲存著非常重要的資訊,如果使用 p2+2 把這部分的內容修改了,程式出現段錯誤退出還好,要是沒有報錯,卻給出了錯誤結果就麻煩了,這種錯誤非常難發現,所以在開發階段就應該小心處理。

    按照上面的分析,在定義區域性指標變數時,如果忘了對它初始化,區域性變數的值是任意的,這也就是說它可能指向任意地方,這時如果使用它,也有可能出現難以發現的錯誤。

    這種指向不確定地址的指標,程式設計師習慣稱為“野指標”。

    為了避免出現野指標,在定義指標變數時就應該給它明確的初值,例如:

    就是把地址0轉換成指標型別,稱為空指標,它的特殊之處在於,作業系統不會把任何資料儲存在地址0及其附近,也不會把地址0~0xfff的頁面對映到物理記憶體,所以任何對地址0的訪問都會立刻導致段錯誤。* p = 0;會導致段錯誤,就像放在眼前的炸彈一樣很容易找到,相比之下,野指標的錯誤就像埋下地雷一樣,更難發現和排除,這次走過去沒事,下次走過去就有事。

  • 2 # TonyDeng

    所謂指標,是一個整型資料,儲存的值被解釋為一個地址,但是,它所指向的地址是什麼型別的資料呢?舉個例子,要用一個指標指向一個double型資料,那麼你要告訴編譯器這個指標指向的是double型資料,到使用的時候,程式到這個地址處讀寫是按double型進行的,即一次讀寫8位元組,如果指向的是一個int,則只讀寫4位元組,如果是指向尺寸為100位元組的物件也是讀寫100位元組。現在你看到沒有,指向的資料是什麼型別,就是指標宣告的型別,必須有的。

    指標本身永遠是一個4位元組的整數(32位指標,64位指標則是8位元組的),這其實沒意義,關鍵是這個地址是一個什麼資料型別的入口地址,真正要用的是這個。

    對指標的運算,比如p++,加1是把地址值按所指向的資料型別的尺寸加值,指向的資料是4位元組的,地址值就加4,是8位元組的加8,100位元組的加100。所以,指標加減是按所指向的資料型別的尺寸加減的。

    明白上面所說的,就明白指標真正有用的地方是記住它指向什麼資料型別,而不是什麼地址。實際上,在真正的程式設計師思維中,指標並不需要是地址,它只是一個指示器,不是地址也可以是指標概念,比如電子表格中的單元座標或資料庫中的記錄序號,都可以理解並表達為指標。學程式設計到必須靈活,不能死板,指標概念不是C語言特有的,更不是什麼地址。

    pointer單詞是意思是指示器,像教鞭那樣的指示器,告訴你看哪裡的,既可以指向人,也可以指向太陽月亮星球。指示而已,它不會自己跑到那裡去,跑是實際操作的那個,東指一下西指一下,當然快,但要把真實的資料取回來,還要差遣跑腿和搬運費,貨量越大費用越高。指標不會給你節省資源,相反更浪費資源(除了實際的資料之外你還要付出儲存地址的指標變數資源,一個指標佔用你4位元組記憶體,是額外的),優勢是動口不動手,間接存取罷了,提取寫入資料有多快是看跑腿的快不快和裝車卸貨快不快。指標最大的用處是指向堆空間的大資料尤其是陣列,資料大搬運費用高,乾脆告訴客戶地址叫他自己去提貨,減少複製和移動代價。

    指標或引用的風險是對真身操作,改動或刪除、覆蓋,都是沒備份的,搞錯了,挽救的餘地都沒有。更嚴重的風險,是用多個指標指向同一個物件,張三改過李四未必知道,王五把物件釋放了張三又去訪問結果找錯了物件(此地址已被政府徵用重新分配)。本質上跟使用全域性變數差別不大。

    指標重要,但不必神化它,什麼學會指標就是高手云云,信他你就傻了。

  • 3 # Gfilsxin

    準確的說,不是地址要區分型別,而是要區分這個地址儲存的資料的型別。

    建議你可以先看看計算機原理方面的書,對記憶體這個概念好好了解一下,不僅瞭解軟體方面的,最好也瞭解一下硬體方面的,如資料匯流排、地址匯流排、儲存單元、DDR等概念。

    C的指標表示地址沒錯,而且這個指標變數在同一個作業系統中佔的記憶體地址是一樣的,32位系統中是4個位元組,64位系統中是8個位元組。

    雖然指標變數佔用的位元組大小是一樣的,但是這個記憶體所儲存的資料型別是可變的,比如int*表示這個地址儲存的是整型資料,佔用4位元組空間;short*表示短整型資料,佔用2位元組空間,等等。

    至於指標的加法,其實也很好理解,指標每增加1,該指標指向的地址實際上會增加該地址記憶體中儲存資料佔用的空間位元組大小。

    例如,int *p=4;執行p+1後,p的值實際會變成8。

  • 中秋節和大豐收的關聯?
  • 小孩為什麼喜歡用牙齒咬東西?