2015-05-02 36 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