【问题标题】:Copy two structs in C that contain char pointers在 C 中复制两个包含 char 指针的结构
【发布时间】:2011-03-19 11:03:42
【问题描述】:

复制两个包含 char 数组的结构的标准方法是什么?

这里有一些代码:

#include stdio.h>
#include string.h>
#include stdlib.h>

typedef struct {
    char* name;
    char* surname;
} person;

int main(void){
    person p1;
    person p2;

    p1.name     = (char*)malloc(5);
    p1.surname  = (char*)malloc(5);

    strcpy(p1.name, "AAAA");
    strcpy(p1.surname, "BBBB");

    memcpy(&p2, &p1, sizeof(person));
    free(p1.name);
    printf("%s\n", p2.name);
    return 0;
}

printf("%s\n", p2.name); 行没有打印任何内容,因为我释放了缓冲区。

我的结构的问题是它们比结构person 大。它们包含数百个 char 指针,我必须一个一个地复制每个成员。

是否有另一种方法可以复制两个包含 char 数组的结构,而不为每个成员使用 mallocstrcpy

【问题讨论】:

  • memcpy 是如何工作的,如果没有为 p2 分配内存,谁能解释一下?它不应该在运行时抛出一些异常吗?
  • 您的结构包含指针,而不是数组。你的指针可能包含一个 char 数组的地址,但是你想复制那个数组,你必须明确地管理它。

标签: c pointers struct


【解决方案1】:

你别无选择,只能自己提供复制功能:

void copy_person(person *dst, const person *src)
{
    dst->name = malloc(strlen(src->name) + 1);
    dst->surname = malloc(strlen(src->surname) + 1);
    strcpy(dst->name, src->name);
    strcpy(dst->surname, src->surname);
}

这可能比这更详细:检查错误,将strlen + strcpy 分解为辅助函数等。

这就是 C++ 中的复制构造函数的用途。

【讨论】:

  • 如果您使用的是 POSIX 系统(例如 linux),您可以使用 strdup 而不是 malloc + strcpy
  • @Jens:当我写下“在辅助函数中分解......
【解决方案2】:

是的,复制包含 char arrays 的结构不会有任何问题,但是带有 char pointers 的结构(或任何类型的指针)你必须手动操作。

另请注意,在 C 中不需要 malloc 的返回类型转换(它在 C++ 中),并且可以隐藏 malloc 丢失的原型。

【讨论】:

    【解决方案3】:

    要详细说明 Alexandre C 的答案。您可能希望将 malloc() 作为单个操作执行,以便 free() 也很简单。

    这种方法提供了一定程度的保护,因为单个malloc() 将成功或失败,因此您不会遇到malloc() 在构建副本中途失败的问题。使用这种方法,您可以将 person 与已分配的指向 person 的指针混合在一起,因此您可能希望有两种不同的数据类型,如下所示,以便更好地标记哪个是哪个。

    我为复制提供了两种选择,一种使用 C 标准库函数 strcpy()strlen(),另一种使用简单的函数,该函数执行直接复制并返回指向目标缓冲区中停止位置的指针.

    我没有尝试编译这个例子,所以它可能有问题。

    这种方法可能存在一个问题。由于各个字符串没有分配,如果您使用它们的指针移动各个字符串,并且认为每个单独的字符串都是它自己的内存分配区域,那么您可能会遇到问题。这种方法假设需要整个对象,或者一个都不需要。

        #include <stdio.h>
        #include <string.h>
        #include <stdlib.h>
    
        typedef struct {
            char* name;
            char* surname;
            char* address1;
        } person, *personptr;
    
        // copy a string to destination string return pointer after end of destination string
        char * StrCpyRetEnd (char *pDest, char *pSrc)
        {
            while (*pDest++ = *pSrc++);
            return pDest;
        }
        personptr DeepCopyPerson (person *pSrc)
        {
            personptr     pDest = 0;
            unsigned int  iTotalSize = sizeof(person);
    
            iTotalSize += (strlen(pSrc->name) + 1) * sizeof(char);
            iTotalSize += (strlen(pSrc->surname) + 1) * sizeof(char);
            iTotalSize += (strlen(pSrc->address1) + 1) * sizeof(char);
            pDest = malloc(iTotalSize);
            if (pDest) {
    #if 1
                // alternative one without a helper function
                pDest->name = (char *)(pDest + 1);  strcpy (pDest->name, pSrc->name);
                pDest->surname = pDest->name + strlen(pDest->name) + 1; strcpy (pDest->surname, pSrc->surname);
                pDest->address1 = pDest->surname + strlen(pDest->surname) + 1; strcpy (pDest->address1, pSrc->address1);
    #else
                // alternative two using StrCpyRetEnd () function
                pDest->name = (char *)(pDest + 1);
                pDest->surname = StrCpyRetEnd (pDest->name, pSrc->name);
                pDest->address1 = StrCpyRetEnd (pDest->surname, pSrc->surname);
                strcpy (pDest->address1, pSrc->address1);
    #endif
            }
            return pDest;
        }
    
        int main(void){
            person    p1;  // programmer managed person with separate mallocs
            personptr p2;  // created using ClonePerson()
    
            p1.name     = malloc(5);
            p1.surname  = malloc(5);
            p1.address1 = malloc(10);
    
            strcpy(p1.name,"AAAA");
            strcpy(p1.surname,"BBBB");
            strcpy(p1.address1,"address1");
    
            p2 = DeepCopyPerson (&p1);
    
            free(p1.name);
            printf("%s\n", p2->name);
    
            free (p2);   // frees p2 and all of the memory used by p2
            return 0;
        }
    

    【讨论】:

      【解决方案4】:

      如果要进行复制,则必须为任何指针分配内存。但是,您始终可以将指针指向已分配的内存。例如,您可以执行以下操作:

      p2.name = p1.name (p1.name is already allocated memory)
      

      这是危险的,因为对同一个内存位置的引用不止一个。如果您释放p1.namep2.name,则会导致危险情况。

      为了复制整个内容,您必须为结构 p2 的指针分配内存。

      p2.name = <allocate memory>
      Copy individual struct members instead of a memcpy of the entire struct
      

      这是因为内存不是以连续方式分配的。 sizeof(struct) 也会给你结构成员的大小,而不是分配给它的内存。

      例如sizeof(p2) = 8 = sizeof(p1)= sizeof(person) 即使在分配内存给p1 的成员之后。

      如果成员是 char 数组,情况就不同了。

      【讨论】:

        【解决方案5】:

        有点开箱即用的想法:

        由于你的结构是静态的,你可以编写一个小的实用程序或脚本来为你生成复制代码。

        将你的结构体定义的源代码作为输入,然后设计一套规则来生成复制代码。

        这是速写,我不知道手动编写复制代码是否更快 - 但至少这是一个更有趣的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-15
          • 2022-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多