首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C 語言指標與陣列深度解析:從入門到精通

     編輯:關於C

前言:為什麼指標讓人「暈」?

許多初學者對指標「望而生畏」,甚至戲稱「見過暈車、暈血的,頭一回見暈指標的」。其實,「暈指標」與「暈車」同理:皆源於基礎不穩、心理恐懼與缺乏系統性理解。克服的關鍵在於「攻心」——建立正確的記憶體模型與變量本質認知,再輔以大量實戰演練。

一、熱身測試:指標轉換陷阱

先看一段經典代碼,若能輕鬆給出輸出,說明功底不錯:

#include <stdio.h>

int main(void)
{
    char* str = "ABCDEFGHIJKL";
    int* pInt = (int*)str;
    printf("%c\n%c\n", *(str+1), *(char *)(pInt+1));
    return 0;
}

解析:str+1 指向 'B';pInt 為 int*,pInt+1 向後偏移 sizeof(int) (通常 4 Bytes),指向 'E'。輸出為 B 和 E。此題考驗指標算術與類型轉換核心概念。

二、基石鞏固:變量與常量的本質

1. 常量

  • 定義:值不可改變的量,又稱字面量。
  • 分類:直接常量 (如 12, 'a', "abc") 與 符號常量 (如 #define PI 3.14)。
  • 核心特徵:編譯期確定,存儲於只讀區,**不可被賦值、不可修改**,僅能讀取。

2. 變量

  • 定義:值可改變的量,必須定義後才能使用。
  • 三要素:名字、類型、值 (存儲於記憶體中的二進制序列)。
  • 名字與地址綁定:編譯期將變量名與記憶體地址綁定。訪問方式有二:變量名直接訪問、或通過地址 (指標) 間接訪問。

3. 變量值的「多面性」

記憶體中只有 0 和 1。**類型決定了解讀方式**:

  • int a = 97; -> 二進制按整數解讀 -> 顯示 97。
  • char a = 'a'; -> 同樣二進制 (ASCII 97) -> 顯示 'a'。
  • int *p = &a; -> 指標變量 p 存儲的二進制被解讀為地址。

核心心法:變量值只是二進制序列,類型賦予其意義。

三、指標變量圖解

int a = 10;
int *p;
p = &a;

指標與變量記憶體關係圖

  • a 位於地址 0x2c406b24,值為 10。
  • p 是指標變量,自身有地址,其**值**為 0x2c406b24 (即 a 的地址)。
  • 訪問 a:a (直接) 或 *p (間接/解引用)。

四、實戰驗證:六道「藥方」

請先思考再看答案:

  1. 類型轉換:char ch='a'; int a=(int)ch; -> ch 值 97, a 值 97。輸出 97 a。
  2. 地址與指標:int add=0x123456; int *p=(int*)add; -> add 值為整數 0x123456; p 值為地址 0x123456; *p 解引用該地址 (危險操作,可能崩潰)。
  3. 宏替換:#define PI 3.14; int a=PI; -> 預處理展開為 int a=3.14;,隱式轉換 a=3。**合法**。
  4. printf("%d", PI); -> 展開為 printf("%d", 3.14);,格式不匹配,**未定義行為**。
  5. 常量不可修改:PI = 1.85; -> 編譯錯誤,宏常量非左值。
  6. 字串常量陷阱:
    char *str = "abcdef";
    printf("%s", str); // 正常
    *str = "fedcba"; // 錯誤!嘗試修改只讀記憶體 -> 段錯誤
    str[2] = 'C';    // 同樣錯誤!修改字串常量內容
    

五、字串常量的「房間與鑰匙」比喻

char *str = "abcdef";:編譯器在只讀區開闢房間存放 "abcdef",將鑰匙 (地址) 交給指標變量 str。你可以用鑰匙進房間**看 (讀)**,但絕不能**動 (寫)** 房間裡的傢俱,否則被「老闆」(作業系統) Kill 掉 (Segmentation Fault)。

六、指標的雙胞胎:陣列

1. 陣列特徵

  • 元素類型相同 (「和諧」)。
  • 記憶體**連續分配**。
  • char a[6] = {'A','B','C','D','E','F'}; 每元素 1 Byte,地址遞增 1。

陣列記憶體佈局圖

2. 陣列名的「三個代表」

陣列名 a 在大多數表達式中退化為指向首元素的指標:

  1. 代表陣列整體 (如 sizeof(a) 計算整個陣列大小)。
  2. 代表陣列首地址 (&a)。
  3. 代表首元素地址 (&a[0],最常見用法)。

3. 指標算術與類型

指標加減看類型:步長 = n * sizeof(類型)。

  • char* +1 -> 地址 +1。
  • int* +1 -> 地址 +4。
  • 一維陣列 a[5] - a[3] = 2 (元素個數差),與類型無關。

4. 二維陣列與指標

char a[3][4]; 編譯器視為「含 3 個元素的一維陣列,每元素是長度 4 的一維陣列」。

二維陣列記憶體圖解

  • a 首地址 = &a[0]。
  • a+1 步長為 sizeof(a[0]) = 4 Bytes (跳過一整行)。

5. 編譯器視角:陣列即指標

編譯器處理陣列下標 a[i] 時,內部轉換為 *(a+i)。**反彙編後不存在變量名,只有地址運算**。

程序執行圖解

上圖代碼輸出為 B 和 E,驗證了指標算術與二維陣列佈局。

總結

  1. **常量唯讀**,變量可讀寫,本質皆為二進制,類型定義解讀規則。
  2. **字串常量存只讀區**,char* 指向它屬危險操作,建議用 const char* 或陣列 char str[]。
  3. **陣列名退化為指標**,但非指標變量 (不可修改、sizeof 不同)。
  4. **指標算術依賴類型**,二維陣列行指標步長為行大小。

理解記憶體模型,指標不再是「毒藥」,而是掌控底層的「利器」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved