計算機中所有的數據都必須放在內存中,不同類型的數據占用的字節數不一樣,例如 int 占用 4 個字節,char 占用 1 個字節。為了正確地訪問這些數據,必須為每個字節都編上號碼,就像門牌號、身份證號一樣,每個字節的編號是唯一的,根據編號可以準確地找到某個字節。
下圖是 4G 內存中每個字節的編號(以十六進制表示):

我們將內存中字節的編號稱為地址(Address)或[指針](Pointer)。地址從 0 開始依次增加,對于 32 位環境,程序能夠使用的內存為 4GB,最小的地址為 0,最大的地址為 0XFFFFFFFF。
下面的代碼演示了如何輸出一個地址:
```
#include <stdio.h>
int main(){
int a = 100;
char str[20] = "c.biancheng.net";
printf("%#X, %#X\n", &a, str);
return 0;
}
```
運行結果:
0X28FF3C, 0X28FF10
`%#X`表示以十六進制形式輸出,并附帶前綴`0X`。a 是一個變量,用來存放整數,需要在前面加`&`來獲得它的地址;str 本身就表示字符串的首地址,不需要加`&`。
> C語言中有一個控制符`%p`,專門用來以十六進制形式輸出地址,不過 %p 的輸出格式并不統一,有的編譯器帶`0x`前綴,有的不帶,所以此處我們并沒有采用。
## 一切都是地址
C語言用變量來存儲數據,用函數來定義一段可以重復使用的代碼,它們最終都要放到內存中才能供 CPU 使用。
數據和代碼都以二進制的形式存儲在內存中,計算機無法從格式上區分某塊內存到底存儲的是數據還是代碼。當程序被加載到內存后,操作系統會給不同的內存塊指定不同的權限,擁有讀取和執行權限的內存塊就是代碼,而擁有讀取和寫入權限(也可能只有讀取權限)的內存塊就是數據。
CPU 只能通過地址來取得內存中的代碼和數據,程序在執行過程中會告知 CPU 要執行的代碼以及要讀寫的數據的地址。如果程序不小心出錯,或者開發者有意為之,在 CPU 要寫入數據時給它一個代碼區域的地址,就會發生內存訪問錯誤。這種內存訪問錯誤會被硬件和操作系統攔截,強制程序崩潰,程序員沒有挽救的機會。
CPU 訪問內存時需要的是地址,而不是變量名和函數名!變量名和函數名只是地址的一種助記符,當源文件被編譯和鏈接成可執行程序后,它們都會被替換成地址。編譯和鏈接過程的一項重要任務就是找到這些名稱所對應的地址。
假設變量 a、b、c 在內存中的地址分別是 0X1000、0X2000、0X3000,那么加法運算`c = a + b;`將會被轉換成類似下面的形式:
0X3000 = (0X1000) + (0X2000);
`( )`表示取值操作,整個表達式的意思是,取出地址 0X1000 和 0X2000 上的值,將它們相加,把相加的結果賦值給地址為 0X3000 的內存
變量名和函數名為我們提供了方便,讓我們在編寫代碼的過程中可以使用易于閱讀和理解的英文字符串,不用直接面對二進制地址,那場景簡直讓人崩潰。
需要注意的是,雖然變量名、函數名、字符串名和數組名在本質上是一樣的,它們都是地址的助記符,但在編寫代碼的過程中,我們認為變量名表示的是數據本身,而函數名、字符串名和數組名表示的是代碼塊或數據塊的首地址。
- 空白目錄
- 第一章 c語言簡介
- 1 通俗地理解什么是編程語言
- 2 C語言究竟是一門怎樣的語言
- 第二章 算法簡介&基本語法
- 1 算法簡介
- 2 C 程序結構
- 3 C 基本語法
- 第三章 數據類型
- 1 數據類型
- 2 變量
- 3 常量
- 第四章 運算符
- 1 算術運算符
- 2 關系運算符
- 3 邏輯運算符
- 4 位運算符
- 5 賦值運算符
- 6 雜項運算符(其他運算符)
- 7 c語言中的運算符優先級
- 第五章 控制流
- 1 判斷語句
- 2 switch語句
- 3 循環語句
- 4 流程控制相關案例
- 第六章 指針
- 1 c語言指針概述
- 2 指針的算術運算
- 3 指針數組
- 4 指向指針的指針
- 5 傳遞指針給函數
- 6 從函數返回指針
- 第七章 函數
- 1 函數的語法
- 2 作用域規則
- 3 形參與實參
- 第八章 數組
- 1 C語言中的數組
- 2 多維數組
- 3 傳遞數組給函數
- 4 從函數返回數組
- 第九章 指針
- 1 分鐘徹底理解C語言指針的概念
- 2 C語言指針變量的定義和使用
- 3 C語言指針變量的運算(加法、減法和比較運算)
- 4 C語言數組指針(指向數組的指針)
- 5 C語言字符串指針(指向字符串的指針)
- 第十章 結構體
- 第十一章 練習
- 第十二章 文件操作
- 第十三章 文件操作2