C编程。如何深度复制一个结构体?

8
我有以下两个结构体,其中“子结构体”具有“rusage结构体”作为一个元素。
然后我创建了两个类型为“child”的结构体,让我们称它们为childA和childB。
如何仅将rusage结构从childA复制到childB?
typedef struct{                         
        int numb;
        char *name;
        pid_t pid;
        long userT;
        long systemT;
        struct rusage usage;
}child;


typedef struct{
    struct timeval ru_utime; /* user time used */
    struct timeval ru_stime; /* system time used */
    long   ru_maxrss;        /* maximum resident set size */
    long   ru_ixrss;         /* integral shared memory size */
    long   ru_idrss;         /* integral unshared data size */
    long   ru_isrss;         /* integral unshared stack size */
    long   ru_minflt;        /* page reclaims */
    long   ru_majflt;        /* page faults */
    long   ru_nswap;         /* swaps */
    long   ru_inblock;       /* block input operations */
    long   ru_oublock;       /* block output operations */
    long   ru_msgsnd;        /* messages sent */
    long   ru_msgrcv;        /* messages received */
    long   ru_nsignals;      /* signals received */
    long   ru_nvcsw;         /* voluntary context switches */
    long   ru_nivcsw;        /* involuntary context switches */

}rusage;

我尝试了以下操作,但我猜测它只是复制了内存地址,因为如果我改变childA中的usage值,它也会在childB中发生改变。
memcpy(&childA,&childB, sizeof(rusage));

我知道这会将childA的所有值都赋给childB。我已经处理了childB中的其他字段,我只需要能够复制驻留在"child"结构体中的rusage结构体,名为usage。


你的所有建议都是为了制作一个与childA完全相同的副本,但如果我改变了childA,childB也会随之改变。 - user69514
你的代码还有其他问题。所有回答你问题的人都是正确的,而且会产生你想要的效果。如果它不起作用,那么问题就在别处。 - Pavel Minaev
@uknown - 这意味着两个结构实例要么位于同一地址(即它们是同一实例),要么在两个不同的地址上更改数据的两个不同实例(或者,您报告的内容有误)。 - ChrisW
2
你确定 struct child 的定义中没有 struct rusage *usage 而是你所展示的内容吗? - caf
7个回答

21

简单来说:

childB.usage = childA.usage;

可能是最简单的 - 可能比memcpy()更快,而且肯定不会更慢。 - Jonathan Leffler
@Jonathan,编译器可能使用了memcpy函数。 - leiz
有人能记得支持这个的最早版本的C是什么,而不必显式地使用memcpy()吗? - Craig McQueen
1
@leiz,我本来会猜测运行时库更有可能依赖于编译器,而不是相反,也就是说(与你所说的相反),memcpy可能是使用与编译器用于分配结构的相同编译器内部函数来实现的。 - ChrisW
1
@leiz:编译器可能使用rep movsb,将结构体复制到结构体中是C语言内置的,如果它在幕后使用 memcpy,那么这可能是故意设计的(即使让C编译器更具可移植性)。 @craig:自从开始,C一直支持它,您不需要#include库memory.h才能执行值类型的内存复制。同样,将结构体分配给间接结构指针也可以正常工作。 - Michael Buen

10

这句话不应该是:

memcpy(&(childB.usage), &(childA.usage), sizeof(rusage))

memcpy中的第一个参数是目标,因此要将A复制到B,需要将A放置在第二个参数的位置。 - Alek Davis
当然,如果您在结构体中有一个指针,您需要做更多的工作来复制值。但是在您的示例中,您想要复制的结构体没有任何指针,因此使用memcpy应该没问题。 - Alek Davis
+1 - 我同意你的逻辑,因为rusage没有任何指针,所以这应该可以正常工作。 - James Black

3

编辑:好的,我误读了问题,你只想复制使用字段;因此我的答案有点不相关。我不删除它,因为它仍然可以提醒初学者在分配或复制包含指针的结构时可能出现别名问题。

当然,memcpy或其他答案中的赋值将起作用。您的结构中唯一的危险来自名称指针。如果您将一个结构体复制到另一个结构体中,那么两个结构体都将包含相同的指针并指向相同的内存。您创建了一个别名。这意味着如果您更改分配空间中的名称,则从其他结构体中可见。此外,如果将您的结构传递给标准释放函数,则存在双重free的危险。

要创建真正的结构副本,您应该执行以下操作:

memcpy(&childA,&childB, sizeof(rusage));    
if(childB.name)
  childA.name = strdup(childB.name);

或者另一种选择是

childA = childB;
if(childB.name)
  childA.name = strdup(childB.name);

0
在这个文件中,我将origine的成员复制到destinazione中,首先只使用赋值和strcpy,然后,我使用memcpy将origine复制到memres中。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct inner
{
    char *parola;
    int n;
} interna;

typedef struct outer
{
    struct inner *ptr;
    int numeroesterno;
} esterna;


struct another
{
    struct inner *ptr;
    int numero;
};    //never forget ; here

int main(void)
{
    esterna *origine; //ptr to structs
    struct another *destinazione;
    struct another *memres;

    char *tmpParola;
    tmpParola = malloc(30*sizeof(char));
    strcpy(tmpParola, "AAAAA");

    interna *tmp;  //remember the TYPEDEF, and don't use struct interna
    tmp = (interna *)malloc(sizeof(struct inner));
    // if you use struct interna in sizeof you get
    //  error: invalid application of ‘sizeof’ to incomplete type ‘struct interna’ 

    tmp->n = 500;
    tmp->parola = tmpParola;

    origine = (esterna *)malloc(sizeof(struct outer));

    origine->numeroesterno = 2;
    origine->ptr = tmp;  //the data structer pointed by tmp has already been allocated and set

    // now I have the structure allocated and set, I want to copy this on destinazione
    destinazione = (struct another *)malloc(sizeof(struct another));

    destinazione->numero = origine->numeroesterno;

    //destinazione->ptr = tmp;  //in this case you don't copy struct inner, it's just a reference

    destinazione->ptr = (interna *)malloc(sizeof(struct inner));
    destinazione->ptr->parola = malloc(sizeof(char)*30);
    strcpy(destinazione->ptr->parola, origine->ptr->parola);
    destinazione->ptr->n = 111;

    //modify origine

    origine->numeroesterno = 9999;
    strcpy(origine->ptr->parola, "parola modificata in origine");

    //print destinazione

    printf("\nparola in destinazione :%s\n", destinazione->ptr->parola);
    printf("\nparola in origine :%s\n", origine->ptr->parola);

    //you can see that destinazione is a copy, because mofifying origine, destinazione deosn't change

    //now we play with memcpy

    memres = (struct another *)malloc(sizeof(struct another));

    memcpy(memres, destinazione, sizeof(destinazione)); //till here, is AAAAA
    strcpy(destinazione->ptr->parola, "parola modificata in destinazione");

    printf("\nmemcpy, numero %d\n", memres->numero);
    printf("\nmemcpy, parola :%s\n", memres->ptr->parola);

    //as you can see from the output, memcpy doesn't make a copy of destinazione:
    //modifying destinazione->ptr->parola after the assignment affects what memres carries with it
    //So from the idea that I got, memcpy just creates the pointers to the originary structure

    free(origine->ptr->parola);
    free(origine->ptr);
    return 0;
}

0

首先,正确的代码是:

memcpy(&childA,&childB, sizeof(child));

其次,这将按原样复制值,因此对于所有那些长时间结构体来说,它是安全的, 但是您拥有的char* name参数将指向相同的原始值。

这将复制整个子进程,而不是“仅从childA复制rusage结构到childB”。 - ChrisW

0

childB.usage = childA.usage

由于您在子结构中拥有整个结构,因此简单的复制就足够了。如果您在子结构中有一个指向rusage结构的指针,那么可能会出现问题。在这种情况下,您需要为childB.usage分配内存,然后进行memcpy操作,以便如果有人修改/删除childA,则childB将不受影响。


0

你可以用两种方式来实现,正如其他人所提到的。

1) childB.usage = childA.usage;
2) memcpy(&childB.usage, &childA.usage, sizeof(rusage));

memcpy的第一个参数是目标地址,第二个参数是源地址,第三个参数是长度(你想要复制多少字节)。从你发布的代码中,你试图将整个childB复制到childA,这不是你想要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接