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

C 語言進階:徹底辨析數組與指標的異同 —— 從聲明、存儲、訪問、參數傳遞四個維度

     編輯:關於C語言

核心結論先行

數組不是指標,指標不是數組。 它們在大多數表達式上下文中表現相似 (退化為指向首元素的指標),但本質完全不同。

一、聲明與定義的匹配陷阱

跨文件聲明必須一致


// file1.c 定義
int arr[10] = {1,2,3,4,5,6,7,8,9,10};

// file2.c 聲明 —— 錯誤!
extern int *arr; // 編譯通過,鏈接通過,運行崩潰/亂碼

// file2.c 正確聲明
extern int arr[]; // 或 extern int arr[10];

原因:

  • int arr[10] 定義:分配 40 字節連續內存,符號 arr 綁定該地址,類型含長度信息。
  • extern int *arr 聲明:編譯器認為 arr 是指標變量 (4/8 字節),去該地址取值 作為指標,實際讀到的是 1 (首元素值) 當地址 → 非法訪問。

二、存儲模型本質區別

特性數組 int a[10]指標 int *p
內存佈局連續 10 個 int (40 字節)1 個指標變量 (4/8 字節),指向某處 int
sizeof40 (整個數組大小)4 或 8 (指標大小)
&a 類型int (*)[10] (指向數組的指標)int ** (指向指標的指標)
地址值&a == &a[0] (數值相等,類型不同)&p != p (指標變量地址 vs 指向的地址)
可修改性不可修改 (非常量左值,a = p 編譯錯誤)可修改 (p = a、p++ 合法)
初始化int a[] = {1,2,3} (編譯器計算長度)int *p = a (指向現有數組)

三、訪問機制差異

a[i] vs p[i] 底層步驟


// 數組訪問 a[i] (假設 a 地址 0x1000)
1. 計算偏移:0x1000 + i * sizeof(int)
2. 直接讀取該地址內容

// 指標訪問 p[i] (假設 p 地址 0x2000, p 值 0x1000)
1. 讀取 p 變量值:0x1000
2. 計算偏移:0x1000 + i * sizeof(int)
3. 讀取該地址內容

指標多一次間接讀取 (取指標值)。現代 CPU 快取下差異微乎其微,但概念上必須清晰。

字符串初始化的隱含區別


char *p = "hello";  // "hello" 在常量區,p 指向它,p 可修改指向,*p 不可修改內容
char a[] = "hello"; // "hello" 複製到棧上數組 a,a 內容可修改,a 不可修改指向
a[0] = 'H'; // OK
p[0] = 'H'; // UB:修改常量區

四、函數參數傳遞:數組退化為指標

C 標準規定:函數參數聲明為數組,一律調整為指向首元素的指標。


// 以下三種聲明完全等價
void func(int a[]);
void func(int a[10]); // 10 被忽略,僅作文檔
void func(int *a);    // 本質

// 調用時
int arr[10];
func(arr); // 實際傳遞 &arr[0] (int*)

後果:

  • 函數內 sizeof(a) 返回指標大小,無法獲得數組長度。
  • 必須額外傳遞長度參數:void func(int *a, size_t n)。
  • 多維數組只有第一維退化:void func(int a[][10]) → void func(int (*a)[10]),第二維長度 必須 保留。

五、const 與 static 的特殊情況


// 文件作用域
static int arr[10]; // 內部鏈接,每編譯單元一份
const int arr[10] = {1}; // C 語言外部鏈接 (需 extern), C++ 內部鏈接

// 函數內部
static int arr[10]; // 靜態存儲期,僅初始化一次
const int arr[10] = {1}; // 自動存儲期,每次調用初始化 (C99 VLA 除外)

六、最佳實踐清單

  1. 跨文件共享數組:.c 定義 int arr[10],.h 聲明 extern int arr[] (不寫長度更通用)。
  2. 函數接口:優先用 void func(int *arr, size_t n),文檔說明 arr 指向至少 n 個元素。
  3. 只讀參數:void func(const int *arr, size_t n) 防止誤修改。
  4. 避免指標算術錯誤:p + i 而非 p++ 在循環中,保持 p 不變便於調試。
  5. 結構體柔性數組:struct { size_t len; int data[]; } 替代指標+單獨分配,提升緩存局部性。

總結:一張表記住本質

場景數組指標
定義分配連續內存分配指標變量
sizeof整體大小指標大小
賦值不可 (非左值)可
參數傳遞退化為指標按值傳遞地址
字符串字面量複製內容指向常量區
動態分配VLA / malloc + 指標訪問原生支援

理解本質,不再被「數組名就是指標」的過度簡化誤導,才是 C 語言進階的關鍵一步。

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