2015-05-02 97 views
3

我有一個簡單的結構包含一個字符串定義爲一個字符數組。我認爲使用賦值運算符將結構的實例複製到另一個實例只會複製存儲在char指針中的內存地址。相反,似乎字符串內容被複制。我把一個很簡單的例子:複製C中的字符串成員的結構

#include <stdio.h> 
#include <string.h> 

struct Test{ 
    char str[20]; 
}; 

int main(){ 

    struct Test t1, t2; 
    strcpy(t1.str, "Hello"); 
    strcpy(t2.str, "world"); 
    printf("t1: %s %p\n", t1.str, (char*)(t1.str)); 
    printf("t2: %s %p\n", t2.str, (char*)(t2.str)); 
    t2 = t1; 
    printf("t2: %s %p\n", t2.str, (char*)(t2.str)); 
    return 0; 
} 

用gcc 4.9.2編譯此代碼我得到:

t1: Hello 0x7fffb8fc9df0 
t2: world 0x7fffb8fc9dd0 
t2: Hello 0x7fffb8fc9dd0 

據我瞭解,經過t2 = t1 t2.str指向它指向相同的內存地址在賦值之前,但現在在該地址內,在t1.str中有相同的字符串。所以在我看來,字符串內容已被自動從一個內存位置複製到另一個位置,這是我認爲C不會做的事情。我認爲這種行爲是由於我宣佈str爲char[]而不是char*而引發的。事實上,試圖直接一個串到另一個t2.str = t1.str分配給了這個錯誤:

Test.c: In function ‘main’: 
Test.c:17:10: error: assignment to expression with array type 
    t2.str = t1.str; 
    ^

這讓我覺得陣列有效治療不同於在某些情況下指針。仍然我無法弄清楚哪些是數組賦值的規則,或者換句話說,爲什麼當我將一個結構體複製到另一個結構體時,爲什麼數組中的數組會被複制,但我不能直接將一個數組複製到另一個結構體中。

+1

*在某些情況下,數組的有效處理方式與指針不同*它們是完全不同的公民。 – user3125367

回答

8

該結構不包含指針,但包含20個字符。 t2 = t1之後,t1的20個字符被複制到t2

+0

是的你是對的,我正在做一些實驗,並且即將達到同樣的結論。謝謝! –

0

C a struct是編譯器知道如何構建內存區域的一種方法。 A struct是一種模板或模板,C編譯器使用該模板或模板計算出如何計算對結構各個成員的偏移量。

第一個C編譯器不允許struct分配,因此人們不得不使用memcpy()函數來分配結構,但是稍後的編譯器會這樣做。 C編譯器將通過複製內存區域的struct區域的字節數來執行struct分配,包括可能爲地址範圍從一個地址添加到另一個地址的填充字節。無論源內存區域發生什麼,都會被複制到目標區域。副本沒有什麼明智的做法。它只是從一個存儲位置複製很多字節的數據到另一個存儲位置。

如果在struct或任何類型的數組中有字符串數組,那麼整個數組將被複制,因爲它是結構的一部分。

如果struct包含指針變量,那麼這些指針變量也將從一個區域複製到另一個區域。這樣做的結果是,您將擁有兩個具有相同數據的結構。每個結構體中的指針變量將具有相似的地址值,這兩個區域是彼此的副本,因此一個結構體中的特定指針將具有與另一個結構體中相應指針相同的地址,並且兩者都將指向相同的位置。

請記住,結構賦值只是將數據的字節從一個內存區域複製到另一個區域。舉例來說,如果我們有一個簡單structchar陣列的C源代碼看起來像:

typedef struct { 
    char tt[50]; 
} tt_struct; 

void test (tt_struct *p) 
{ 
    tt_struct jj = *p; 

    tt_struct kk; 

    kk = jj; 
} 

彙編由Visual Studio的列表輸出在調試模式下2005 C++編譯器的kk = jj;的分配是這樣的:

; 10 : tt_struct kk; 
; 11 : 
; 12 : kk = jj; 

    00037 b9 0c 00 00 00 mov  ecx, 12   ; 0000000cH 
    0003c 8d 75 c4  lea  esi, DWORD PTR _jj$[ebp] 
    0003f 8d 7d 88  lea  edi, DWORD PTR _kk$[ebp] 
    00042 f3 a5  rep movsd 
    00044 66 a5  movsw 

該位代碼將4字節字的數據從內存中的一個位置複製到另一個位置。如果數組大小較小,編譯器可能會選擇使用不同系列的指令來複制內存,使其更有效。

在C數組中沒有真正的智能處理。數組並不像Java看到數組那樣被視爲數據結構。在Java中,數組是由一組對象組成的一種對象。在C中,數組只是一個內存區域,數組名稱實際上就像一個常量指針或一個無法更改的指針。結果是在C語言中,你可以有一個數組說明int myInts[5];,Java可以看作是一個由5個整數組成的數組,但是對於C來說,這實際上是一個標籤爲myInts的常量指針。在Java中,如果嘗試訪問超出範圍的數組元素,請說myInts [i],其中我的值爲8,則會收到運行時錯誤。在C語言中,如果你嘗試訪問一個超出範圍的數組元素,比如myInts [i],我的值是8,那麼除非你正在使用一個好的C編譯器進行調試構建,否則你不會得到運行時錯誤運行時檢查。然而,有經驗的C程序員傾向於將數組和指針視爲類似的結構,儘管數組作爲指針確實有一些限制,因爲它們是常量指針的形式,並不完全是指針,但具有與指針相似的一些特徵。

這種緩衝區溢出錯誤在C中通過訪問超過其元素數量的數組非常容易。典型的例子是將字符數組的字符串拷貝到另一個字符數組中,並且源字符數組中沒有零終止字符,當您期望十或十五個字符時,會產生幾百字節的字符串拷貝。

0

實際上有你的情況20個字符,它一樣的,如果你聲明結構作爲struct Test {char c1, char c2, ...}

如果你只想指針複製到字符串,可以更改爲下面的結構聲明和手動管理通過函數和Test_delete的字符串內存。

struct Test{ 
    char* str; 
}; 

void Test_init(struct Test* test, size_t len) { 
    test->str = malloc(len); 
} 

void Test_delete(struct Test* test) { 
    free(test->str); 
} 
0

如果您運行下面的簡單程序

#include <stdio.h> 

int main(void) 
{ 
    { 
     struct Test 
     { 
      char str[20]; 
     }; 
     printf("%zu\n", sizeof(Test)); 
    } 

    { 
     struct Test 
     { 
      char *str; 
     }; 
     printf("%zu\n", sizeof(Test)); 
    } 
    return 0; 
} 

你會得到類似下面的

20 
4 

結果所以第一個結構包含,而20個元素的字符數組第二個結構只包含一個類型爲char *的指針。

當一個結構被分配給另一個結構時,它的數據成員被複制。所以對於第一個結構,數組的所有內容都被複制到另一個結構中。對於第二個結構,只複製指針的值(它包含的地址)。由指針指向的內存不被複制,因爲它不包含在結構本身中。

雖然通常表達式中的數組名稱(極少數例外)轉換爲指向其第一個元素的指針,但數組並非指針。

+0

prin ** t ** f ..... – ace