5

我有一个带有一些指针作为成员的结构,我正在尝试做 memcpy,有人建议我在这种情况下不应该使用 memcpy,因为 memcpy 做的是浅拷贝(意味着它复制指针)而不是深拷贝(意味着复制什么指针指向)。

但我不确定为什么它在以下程序中没有任何区别:请查看代码和输出,并解释为什么在这种情况下它不是浅拷贝?

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

struct student {
    char *username;
    char *id;
    int roll;
};

void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);

int main (void) {
    struct student *student1;
    char *name = "ram";
    char *id = "200ABCD";
    int roll = 34;

    student1 = (struct student *)malloc(sizeof(struct student));
    student1->username = name; 
    student1->id = id;
    student1->roll = roll; 
    print_struct_addr(student1);
    print_struct(student1);
    changeme(student1);
    print_struct(student1);
    print_struct_addr(student1);
    return 0;
}

void print_struct(struct student *s) {
    printf("Name: %s\n", s->username);
    printf("Id: %s\n", s->id); 
    printf("R.No: %d\n", s->roll);
    return; 
}

void print_struct_addr(struct student *s) {
    printf("Addr(Name): %x\n", &s->username);
    printf("Addr(Id): %x\n", &s->id);
    printf("Addr(R.No): %x\n", &s->roll);
    return;
}

void changeme(struct student *s) {
    struct student *student2;
    student2->username = "someone";
    student2->id = "200EFGH";
    student2->roll = 35;
    print_struct_addr(student2);
    memcpy(s, student2, sizeof(struct student));
    student2->username = "somebodyelse";
    return;
}

输出:

Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010
Name: ram
Id: 200ABCD
R.No: 34
Addr(Name): fa163c
Addr(Id): fa1640
Addr(R.No): fa1644
Name: someone
Id: 200EFGH
R.No: 35
Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010

如果 memcpy 进行浅拷贝,为什么 student1->username 不是“somebodyelse”。

请说明在哪种情况下,此代码会产生问题,我希望在 main() 中调用 changeme() 后 student1 中的 student2 信息,并且之后应该能够使用修改后的 student1 数据。

有人建议我不要在这里使用 memcpy() ,但它似乎工作正常。

谢谢

这是修改后的代码:但我仍然没有在这里看到浅拷贝的概念:

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

struct student {
    char *username;
    char *id;
    int roll;
};

void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);

int main (void) {
    struct student *student1;
    char *name = "ram";
    char *id = "200ABCD";
    int roll = 34;

    student1 = malloc(sizeof(*student1));
    student1->username = name; 
    student1->id = id;
    student1->roll = roll; 
    print_struct_addr(student1);
    print_struct(student1);
    changeme(student1);
    print_struct(student1);
    print_struct_addr(student1);
    return 0;
}

void print_struct(struct student *s) {
    printf("Name: %s\n", s->username);
    printf("Id: %s\n", s->id); 
    printf("R.No: %d\n", s->roll);
    return; 
}

void print_struct_addr(struct student *s) {
    printf("Addr(Name): %x\n", &s->username);
    printf("Addr(Id): %x\n", &s->id);
    printf("Addr(R.No): %x\n", &s->roll);
    return;
}

void changeme(struct student *s) {
    struct student *student2;
    student2 = malloc(sizeof(*s));
    student2->username = strdup("someone");
    student2->id = strdup("200EFGH");
    student2->roll = 35;
    print_struct_addr(student2);
    memcpy(s, student2, sizeof(struct student));
    student2->username = strdup("somebodyelse");
    free(student2);
    return;
}
4

3 回答 3

11

这个:

struct student *student2;
student2->username = "someone";
student2->id = "200EFGH";
student2->roll = 35;

正在写入未分配的内存,调用未定义的行为。在写作之前,您需要确保student2指向有效的地方。

要么分配它,要么使用堆栈上的实例,因为无论如何你只是要从中复制。

当然,这整个初始化student2然后s用它覆盖的业务是不必要的复杂,你应该直接修改s

另外,这个:

student1 = (struct student *)malloc(sizeof(struct student));

最好用 C 写成:

student1 = malloc(sizeof *student1);

这消除了无意义的(并且有潜在危险的)强制转换,并确保大小适合该类型,将程序员检查的依赖项替换为编译器处理的依赖项。

第三,初级 C 程序员有点典型的“症状”是没有意识到您可以分配结构。所以,而不是

memcpy(s, student2, sizeof *s);

你可以写:

*s = *student2;

并让编译器正确。这可能是性能上的胜利,因为结构可以包含很多填充,赋值可以知道而不是复制,但memcpy()不能忽略。

于 2012-10-30T12:07:26.047 回答
2

它完全起作用是侥幸。在您的changeme()函数中,您正在为 创建一个新指针student2,但您没有为它分配内存。

其次,在同一个功能中student2 您将其复制到s. 浅拷贝并不意味着拷贝中的任何指针都是共享的——它意味着指针本身的值也被拷贝。所以当你改变student2->username之后memcpy它不会改变s->username.

随着您的进步,您还需要更加小心地分配这些结构中的内存。AFAICR,如果您使用常量文字字符串,那么指针将指向程序内存空间中的一块静态初始化数据。然而,更严格的设计将为这些元素malloc()提供free()动态记忆。如果您需要一个静态初始化的值,您可以使用strdup()或类似的方法将字符串从静态空间复制到堆内存中。

于 2012-10-30T12:07:02.480 回答
0

复制后将用户名设置为“somebodyelse” 。这仅更改函数“changeme()”内的本地副本。尝试在“changeme()”中打印出 student2 ,你会明白我的意思。

于 2012-10-30T12:06:59.893 回答