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

C 語言陣列與指標組合語言深度分析:存取效率差異與 extern 陷阱

     編輯:更多程式設計知識

陣列與指標在 C 語言中常被混為一談,但編譯器產生的組合語言指令卻有本質差異。本文以 VC6.0 Debug 模式輸出為例,逐行註解陣列索引存取 vs 指標間接存取的組合碼差異,並揭示 extern 宣告時「陣列 vs 指標」型別不匹配導致的連結錯誤與執行期崩潰風險。

測試程式碼


#include "stdafx.h"

int main(int argc, char* argv[]) {
    char a = 1;
    char c[] = "1234567890";   // 陣列:堆疊上分配 12 bytes(含結束符)
    char *p = "1234567890";    // 指標:指向唯讀資料段字串常值
    a = c[1];                  // 陣列存取
    a = p[1];                  // 指標存取
    return 0;
}

關鍵組合語言註解

陣列初始化:逐字元搬移到堆疊


; char c[] = "1234567890";
0040102C mov eax, [string "1234567890" (0042201c)]   ; 讀前 4 bytes "1234"
00401031 mov dword ptr [ebp-10h], eax                ; 存入 c[0..3]
00401034 mov ecx, dword ptr [string "1234567890" 4 (00422020)] ; "5678"
0040103A mov dword ptr [ebp-0Ch], ecx                ; 存入 c[4..7]
0040103D mov dx, word ptr [string "1234567890" 8 (00422024)] ; "90"
00401044 mov word ptr [ebp-8], dx                    ; 存入 c[8..9]
00401048 mov al, [string "1234567890" 0Ah (00422026)] ; "\0"
0040104D mov byte ptr [ebp-6], al                    ; 存入 c[10]

陣列內容被複製到堆疊框架(ebp-10h 起始),佔用 12 bytes。

指標初始化:僅存儲地址


; char *p = "1234567890";
00401050 mov dword ptr [ebp-14h], offset string "1234567890" (0042201c)

ebp-14h 僅存放字串常值的首地址(4 bytes),不複製內容。

陣列存取 a = c[1]:2 步驟


00401057 mov cl, byte ptr [ebp-0Fh]   ; 直接計算位移 ebp-10h+1 → 讀取
0040105A mov byte ptr [ebp-4], cl     ; 存入 a

編譯器靜態知道陣列基址(ebp-10h),直接以位移存取,**無間接層級**。

指標存取 a = p[1]:3 步驟


0040105D mov edx, dword ptr [ebp-14h] ; 1. 取得指標值(地址)
00401060 mov al, byte ptr [edx + 1]   ; 2. 間接位址 + 位移讀取
00401063 mov byte ptr [ebp-4], al     ; 3. 存入 a

需先載入指標值,再間接定址,**多一條指令**。

效能結論

操作指令數記憶體存取備註
陣列索引 c[i]21 次(直接位移)基址為編譯期常數
指標索引 p[i]32 次(取指標 + 間接)基址為執行期變數

在最佳化開啟(Release)下,編譯器可能將指標值保持在暫存器中,差距縮小;但陣列存取永不慢於指標。

extern 陷阱:陣列 vs 指標型別不匹配

正確:extern 陣列


// inc.h
char chTest[10] = "123456789";

// test.cpp
#include "inc.h"
extern char chTest[10];  // 型別匹配:char[10]

int main() {
    printf("chTest=%s\n", chTest);  // 正常
    return 0;
}

錯誤:extern 指標


// test.cpp
extern char *chTest;  // 型別不匹配:char* vs char[10]

編譯錯誤:redefinition; different types of indirection

根本原因

  • char chTest[10]:符號 chTest 代表陣列首地址(類型 char[10],大小 10)。
  • char *chTest:符號 chTest 代表指標變數(類型 char*,大小 4/8),其內容儲存地址。
  • 存取方式不同:

// 陣列:基址 + 位移
mov al, byte ptr [chTest + 1]

// 指標:載入指標值 + 間接
mov eax, dword ptr [chTest]
mov al, byte ptr [eax + 1]

若強制連結通過(如關閉型別檢查),執行時會將陣列首地址當作指標值解讀,導致野指標存取、記憶體損毀、程式崩潰。

最佳實務建議

  1. 優先使用陣列語意:char buf[100] 而非 char *buf = malloc(100)(除非需動態大小)。
  2. extern 宣告必須完全匹配定義型別:陣列對陣列、指標對指標。
  3. 跨檔案共用字串常值:定義 const char *const STR = "...",extern 同型別。
  4. 開啟編譯器警告:-Wall -Wextra -pedantic(GCC/Clang)或 /W4(MSVC)可捕獲型別不匹配。

結語

陣列與指標在組合語言層面的差異,揭示了 C 語言「陣列非指標」的本質:陣列是連續記憶體區塊,指標是儲存地址的變數。理解這一點,不僅能寫出更高效的程式碼,更能避免 extern 型別不匹配這類隱蔽且嚴重的錯誤。

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