數組的參數傳遞--參數退化隱式轉換

參數傳遞---關於數組的退化

數組的參數傳遞

在說二維數組前先回顧一下一維數組的參數傳遞,對二維數組的解引用、指針數組、數組指針不是很瞭解的可以先看一下這篇隨筆:二維數組(解引用、指針數組、數組的指針)

一維數組作爲實參傳入函數時,接收的形參有兩種形式

第一種形式:

#include <stdio.h> 
void fun(char s[]);
int main()
{
    char num[10] = "Hello";
    fun(num);

    return 0;
}

void fun(char s[])
{
    puts(s);
}

 

 

第二種形式:

 

#include <stdio.h>

void fun(char s[]);
int main()
{
    char num[10] = "Hello";
    fun(num);

    return 0;
}
 
void fun(char *s)
{
    puts(s);
}

 

下面分析下第一種,因爲當數組作爲實參進行傳遞時會自動退化爲指針(是一種隱式轉換),所以傳入的是一維數組num的首地址,即&num[0],作爲接收的形參char s[]也會自動退化爲char *類型的指針,所以數組在進行傳遞時

傳遞的是數組的地址而不是數組的元素,因爲一維數組的形參是會自動退化爲指針的,所以數組長度填不填都無所謂,形參數組的長度是大於實參的長度還是小於實參的長度都沒有影響(既然的都退化了,指針又沒有像數組長度這個概念所以填不填長度都無所謂了)

 

那麼爲什麼C語言不允許直接傳遞數組的所有元素呢?

數組是一系列數據的集合,數據的數量沒有限制,可能很少,也可能出乎意料的大,對它們進行內存拷貝有可能是一個漫長的過程,會嚴重拖慢程序的效率,爲了防止技藝不佳的程序員寫出低效的代碼,C語言沒有從語法上支持數據集合的直接賦值。

 

一維數組參數傳遞中的坑

 先上代碼:

#include <stdio.h>

int main()
{
    double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    char **p;

    printf("%d\n", sizeof(num));
    printf("%d\n", sizeof(num+0));
    printf("%d\n", sizeof(*num));
    printf("%d\n", sizeof(&num));
    printf("%d\n", sizeof(p));

    return 0;
}

 

讀者可以先想一下結果應該是什麼,再往下看

運行結果如下:

第一個sizeof(num),大部分人應該都知道,應該是數組num的長度*類型所佔字節數,結果爲80(說明在使用sizeof時數組沒有退化,也間接說明了sizeof不是函數而是關鍵字

第二個sizeof(num+0),這個就帶有一定的迷惑性了,num+0包含了一層隱式轉化,轉換後變成了char *類型,結果爲4

第三個sizeof(*num),這比較好理解,就是sizeof(num[0]),結果爲8

第四個sizeof(&num),num本身就是數組的首地址,也就是&num[0],再前面再加一個取地址符,也就是地址的地址,結果爲4,注意&num的類型爲double (*p)[10],如果要存儲&num的值,

需要用double (*)[10]類型的變量(一般不這麼用,沒什麼意義至於爲什麼沒意義下面會細說)

關於這種數組地址的地址,下面上代碼實測一下

 

#include <stdio.h>

int main()
{
   double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
   printf("%p\n%p", num, &num);

   return 0;
}

 

 運行結果爲:

從運行結果可以看出,數組首地址的地址的值數組首地址的值是一模一樣的,這也就是爲什麼上面說沒什麼意義的原因

第五個sizeof(p),p是一個二級指針,和一級指針相同,都是佔4個字節,結果爲4,(讀者可以嘗試下無論多少級指針,只要編譯器的環境是32位的佔的字節數都是4)

 二維數組

二維數組作爲實參傳入函數時,接收的形參形式有如下幾種

第一種:

 

 1 #include <stdio.h>
 2 
 3 void fun(char p[][6]);
 4 
 5 int main()
 6 {
 7     char ss[2][6] = {"hello", "hi"};
 8 
 9     fun(ss);
10     return 0;
11 }
12 
13 void fun(char p[][6])
14 {
15     puts(p[0]);
16     puts(p[1]);    
17 }

 

 

第二種:

 

#include <stdio.h>

void fun(char p[][6]);

int main()
{
    char ss[2][6] = {"hello", "hi"};

    fun(ss);
    return 0;
}

void fun(char (*p)[6])
{
    puts(p[0]);
    puts(p[1]);    
}

 二維數組在作爲實參進行傳遞時也是會退化的,二維數組ss[2][6]會退化成ss(*)[6]類型數組的指針,同樣如果形參也寫成二維數組的形式,就像第一種那樣char p[][6](注意第二維不能省略,不能省略是因爲內存的尋址方式的特點所決定的),也會自動隱式轉化成char (*p)[6]這樣的形式

 

在C99中對指針的退化進行了說明:

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type

“array of type” is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

 

上面這句話說的意思是, 數組在除了3種情況外, 其他時候都要"退化"成指向首元素的指針.
比如對 char s[10] = "Hello";
這3中例外情況是:
(1) sizeof(s)
(2) &s;
(3) 用來初始化s的"Hello";

 (tips:數組的首地址是常量,不可更改,指針保存的地址是變量,可以更改)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章