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

C 專家編程:為何陣列參數退化為指標——記憶體佈局鐵證

     編輯:關於C

核心結論

C 標準規定:**函數參數宣告為陣列時,自動調整為指向首元素的指標**。因此 void f(char ca[]) 與 void f(char *pa) 在編譯器眼中完全等價,sizeof(ca) 回傳指標大小而非陣列大小。

實驗代碼

#include <stdio.h>

void print_array_test(char ca[]) {
    printf("ca          : %s\n", ca);
    printf("&ca         : %p\n", (void*)&ca);        // 參數自身位址(棧上)
    printf("&ca[0]      : %s\n", &ca[0]);      // 首元素位址 = 陣列位址
    printf("&ca[1]      : %s\n", &ca[1]);      // 次元素位址
    printf("*(ca+0)     : %c\n", *(ca+0));
    printf("*(ca+1)     : %c\n", *(ca+1));
    printf("ca+1        : %s\n", ca+1);
}

void print_ptr_test(char *pa) {
    printf("pa          : %s\n", pa);
    printf("&pa         : %p\n", (void*)&pa);        // 參數自身位址
    printf("&pa[0]      : %s\n", &pa[0]);
    printf("&pa[1]      : %s\n", &pa[1]);
    printf("pa+1        : %s\n", pa+1);
    printf("pa[1]       : %c\n", pa[1]);
    printf("*(pa+1)     : %c\n", *(pa+1));
    printf("++pa        : %s\n", ++pa);         // 指標可修改
}

int main() {
    char abc[] = "hello,world!";
    print_array_test(abc);
    print_ptr_test(abc);
    return 0;
}

執行結果與分析

ca          : hello,world!
&ca         : 0x7ffd...   // 不同於 &ca[0],證明 ca 是區域指標變數
&ca[0]      : hello,world!
&ca[1]      : ello,world!
*(ca+0)     : h
*(ca+1)     : e
ca+1        : ello,world!

pa          : hello,world!
&pa         : 0x7ffd...   // 與 &ca 相同位置(同一棧框)
&pa[0]      : hello,world!
&pa[1]      : ello,world!
pa+1        : ello,world!
pa[1]       : e
*(pa+1)     : e
++pa        : ello,world!  // pa 可自增,ca 則不可

關鍵觀察點

  1. &ca ≠ &ca[0]:&ca 是函數參數(指標變數)在棧上的位址,&ca[0] 是實際陣列首元素位址(呼叫端記憶體)。兩者數值不同,徹底證明 ca 是指標而非陣列。
  2. ca[1] ≡ *(ca+1) ≡ pa[1] ≡ *(pa+1):下標運算子 [] 在指標上同樣有效,本質皆為指標算術。
  3. ++pa 合法,++ca 非法:參數 ca 雖是指標,但屬區域變數,C 標準禁止修改參數陣列宣告的指標值(雖部分編譯器允許並發出警告)。

標準條文依據 (C99 6.7.5.3/7)

A declaration of a parameter as ''array of type'' shall be adjusted to ''qualified pointer to type''...

編譯器在語義分析階段即完成調整,故函數體內完全看不見原始陣列大小資訊。

實務建議

  • 若需在函數內知道陣列長度,**必須額外傳遞長度參數**。
  • 使用 void f(char *pa, size_t len) 或 void f(char pa[], size_t len) 兩種寫法效果相同,前者語意更清晰。
  • 若需保護陣列不被修改,請加 const:void f(const char *pa, size_t len)。

延伸:二維陣列參數

void f(int a[3][4]);   // 調整為 int (*a)[4] —— 指向 4 元素 int 陣列的指標
void f(int a[][4]);    // 同樣,第一維退化,第二維保留
void f(int *a[4]);     // 這是「指標陣列」,非二維陣列!

只有**最左維**會退化,後續維度資訊保留在指標型別中,這也是為何動態分配二維陣列常用「指標陣列」或「單一連續區塊 + 手動索引」的原因。

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