訂閱
糾錯
加入自媒體

為什么數(shù)組不可以直接賦值,結(jié)構(gòu)體中的數(shù)組卻可以?

一、前言

二、數(shù)組的各種操作

1. 錯誤方式

2. 利用結(jié)構(gòu)體來復制數(shù)組

3. 其他復制方式

三、語言標準和編譯器

1. 數(shù)組和指針的關(guān)系

2. 為什么不能對數(shù)組賦值

3. 函數(shù)形參是數(shù)組的情況

4. 為什么結(jié)構(gòu)體中的數(shù)組可以復制

5. 參數(shù)傳遞和返回值

五、總結(jié)

一、前言

在 C/C++ 語言中,數(shù)組類型的變量是不可以直接賦值的。但是如果把數(shù)組放在結(jié)構(gòu)體中,然后對結(jié)構(gòu)體變量進行賦值,就可以實現(xiàn)把其中的數(shù)組內(nèi)容進行復制過去。

很多朋友對這個不是特別理解,只是強制記憶,下面我嘗試用自己的理解來描述一下,希望對你有所幫助!

二、數(shù)組的各種操作 

1. 錯誤代碼int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a;

對于上面的賦值語句,編譯器會報錯 error: assignment to expression with array type,即:不能對一個數(shù)組類型的變量進行賦值。

那么編譯器此時是如何來解釋 a  和 b 的?下面會說到這個問題。

有一個地方提一下:第一條語句中的 = 操作,不是賦值,而是初始化。C/C++ 語法規(guī)定在定義變量的時候,是可以使用 操作符 = 來進行初始化操作的。

2. 利用結(jié)構(gòu)體來復制數(shù)組typedef struct {
   int arr[5];
} array_wrap;
array_wrap a = {{1, 2, 3, 4, 5}};
array_wrap b;
b = a;

這里的賦值操作是針對結(jié)構(gòu)體變量,C 語言標準允許這種行為,是合法的,變量 a 中的所有內(nèi)容(也就是這個變量占用過的那一塊內(nèi)存空間中的內(nèi)容)會原樣的復制到變量 b 中。

3. 其他復制方式

既然不能直接對數(shù)組類型的變量進行賦值,只能尋求其他的替代方式,例如:

利用 memcpy(b, a, sizeof(int) * 5); 復制一整段內(nèi)存空間中的內(nèi)容;利用 for/while 等循環(huán)語句,逐個復制數(shù)組中每一個元素: b[i] = a[i];

三、語言標準和編譯器

C/C++ 只是一門高級語言,是被標準委員會從無到有設計出來的,因此我們編程時需要嚴格遵守這些規(guī)則。

這些規(guī)則中,就包括這么一條:只有標量和結(jié)構(gòu)體,才能出現(xiàn)在賦值操作符=的左側(cè)。

但是數(shù)組類型并不是一個標量,因此不能對結(jié)構(gòu)體執(zhí)行賦值操作。

理論上,如果 C/C++ 語言愿意的話,是"可以"對數(shù)組直接賦值的(那就要修改語法標準),只不過標準委員會在經(jīng)過各種場景的權(quán)衡利弊之后,做出了目前這樣的規(guī)定,這是對各種考慮到的因素進行權(quán)衡之后的結(jié)果。

也就是說,目前標準中對于數(shù)組操作的方式,是利大于弊。

既然標準已經(jīng)是制定成這樣的了,我們就來分析一下編譯器是如何來遵循、實現(xiàn)這個標準的。

1. 數(shù)組與指針的曖昧關(guān)系

很多人都這樣記憶:數(shù)組名就是數(shù)組開始地址的指針。這是不對的,或者說不嚴謹?shù)摹?/p>

在 C/C++ 中,數(shù)組就是數(shù)組,指針就是指針。數(shù)組在內(nèi)存中有確定的空間(每個元素的大小 x 元素個數(shù))。

只不過在表達式中,數(shù)組名會“臨時的”表示數(shù)組中第一個元素的常量指針(前提條件:在沒有操作符 sizeof 和 & 的情況下)。

對于下面這段代碼,打印結(jié)果是相同的:

int a[5] = {1, 2, 3, 4, 5};
printf("a = %p ", a);
printf("&a = %p ", &a);

第一個 printf 中,a 會“臨時的”代表指向第一個元素的常量指針。

第二個 printf 中,a 就表示一個數(shù)組,與指針沒有半毛錢的關(guān)系,前面加上取地址符 &,就表示獲取這個數(shù)組所在的地址,這個地址與第一個元素的地址是重合的。

注意:代碼在被編譯成二進制文件之后,沒有任何變量的概念,全部是用地址來“傳遞” C/C++ 代碼中的變量。

2. 為什么不能對數(shù)組變量賦值

有了上面的基礎(chǔ)理解就好辦了,對于下面的這段代碼:

int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a;

在賦值語句 b = a 中,左側(cè)的 b 是一個數(shù)組類型,右側(cè)的 a 被編譯器“臨時的”代表第一個元素的常量指針,但是數(shù)組不是一個標量,不可以放在賦值運算符=的左側(cè),因此編譯器就抱怨:非法!

既然在一個表達式中,數(shù)組名被臨時的表示第一個元素的常量指針,那么就說明我們不能對數(shù)組名本身進行計算,例如:不能進行 a++, a-- 等操作。

例如:下面這的遍歷方式是非法的:

int a[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++)
{
   // 常量指針,不可以進行遞增操作
   printf("a[%d] = %d ", i, *a++);
}
3. 函數(shù)形參是數(shù)組的情況

考慮下面這個函數(shù):

void func(int arr[5])
{
for (int i = 0; i < 5; ++i)
{
printf(*arr++);  // 合法!
}
}

形參 arr 在形式上好像是一個數(shù)組,實際上被編譯器當做指針,也就是相當于:void func(int *arr),因此,在 printf 打印語句中,可以對 arr 進行遞增操作。

PS: 這種場景下都需要額外的傳遞一個參數(shù),來告知元素的個數(shù)。

調(diào)用這個函數(shù)的代碼如下:

int a[5] = {1, 2, 3, 4, 5};
fun(a);

數(shù)組名臨時代表第一個元素的常量指針,在傳參的時候,形參 arr 的值就是數(shù)組中第一個元素的內(nèi)存地址。

4. 為什么結(jié)構(gòu)體中的數(shù)組可以復制

有了前面的語法標準,這個問題似乎不用再討論了~~

賦值的目的是什么?就是讓一塊內(nèi)存空間的內(nèi)容,與另一塊內(nèi)存空間中的內(nèi)容完全相同。如果想要完成復制操作,那么就需要知道這塊內(nèi)存空間的大小。

編譯器是知道一個結(jié)構(gòu)體變量所占用的空間大小的,所以當復制的時候,類似于 memcpy 一樣,把一個結(jié)構(gòu)體變量所占空間按照 byte to byte 的方式復制過去。

5. 參數(shù)傳遞和返回值在調(diào)用函數(shù)時,實參到形參的傳遞;函數(shù)執(zhí)行結(jié)束后的返回值;

這兩個場景中都涉及到變量的賦值問題。

關(guān)于參數(shù)傳遞,上面已經(jīng)說了:編譯器是把形參當做普通的指針類型的。

對于函數(shù)返回值來說,同樣的道理,也不能直接返回一個數(shù)組,因為它僅僅是臨時性的代表第一個元素的常量指針。

當然,可以利用結(jié)構(gòu)體的可賦值特性,把數(shù)組包裹在其中,以此達到復制的效果。

五、總結(jié)

記住這兩句話:

1.數(shù)組就是數(shù)組,指針就是指針,它們各不相干。

2.在表達式中,數(shù)組名會“臨時的”表示數(shù)組中第一個元素的常量指針(前提條件:在沒有操作符 sizeof 和 & 的情況下)

聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

    掃碼關(guān)注公眾號
    OFweek人工智能網(wǎng)
    獲取更多精彩內(nèi)容
    文章糾錯
    x
    *文字標題:
    *糾錯內(nèi)容:
    聯(lián)系郵箱:
    *驗 證 碼:

    粵公網(wǎng)安備 44030502002758號