【问题标题】:Assign one struct to another in C在 C 中将一个结构分配给另一个结构
【发布时间】:2011-01-19 03:06:38
【问题描述】:

你能将一个结构的一个实例分配给另一个实例吗,像这样:

struct Test t1;
struct Test t2;
t2 = t1;

我已经看到它适用于简单的结构,但是它适用于复杂的结构吗?
编译器如何知道如何根据数据项的类型复制数据项,即区分int 和字符串?

【问题讨论】:

    标签: c struct


    【解决方案1】:

    是的,如果结构是相同的类型。将其视为记忆副本。

    【讨论】:

    • 记住没有深拷贝,指向内存没有被拷贝。
    • 并发在这里也是一个问题。
    • @Tim 并发对于分配内置类型(如整数和双精度数)而言不再是一个问题 - 分配也不是这些的原子操作。
    • 好的,如果创建了副本,我可以稍后使用 free() 释放内存吗?
    • @Betlista 您无法使用 free() 释放内存,因为它们是自动变量:en.wikipedia.org/wiki/Automatic_variable
    【解决方案2】:

    是的,结构支持赋值。不过也有问题:

    struct S {
       char * p;
    };
    
    struct S s1, s2;
    s1.p = malloc(100);
    s2 = s1;
    

    现在两个结构的指针都指向同一个内存块——编译器不会复制指向的数据。现在很难知道哪个结构实例拥有数据。这就是 C++ 发明用户可定义赋值运算符概念的原因——您可以编写特定的代码来处理这种情况。

    【讨论】:

    • 我提高了它,因为阅读它让我意识到我自己的答案中的错误/遗漏。
    • +1 表示实际上没有进行任何复制。
    • 为什么这被标记为垃圾邮件?有人失去了对鼠标的控制吗?
    • @gf 显然也很冒犯!
    • @rahmanisback anon 的回答对这个话题非常清楚:“编译器不会复制指向的数据”。 struct 本身的数据是明显复制的。
    【解决方案3】:

    先看这个例子:

    下面给出了一个简单的 C 程序的 C 代码

    struct Foo {
        char a;
        int b;
        double c;
    } foo1, foo2;
    
    void foo_assign(void)
    {
        foo1 = foo2;
    }
    
    int main(/*char *argv[],int argc*/)
    {
        foo_assign();
        return 0;
    }
    

    foo_assign() 的等效 ASM 代码是

    00401050 <_foo_assign>:
      401050:   55                      push   %ebp
      401051:   89 e5                   mov    %esp,%ebp
      401053:   a1 20 20 40 00          mov    0x402020,%eax
      401058:   a3 30 20 40 00          mov    %eax,0x402030
      40105d:   a1 24 20 40 00          mov    0x402024,%eax
      401062:   a3 34 20 40 00          mov    %eax,0x402034
      401067:   a1 28 20 40 00          mov    0x402028,%eax
      40106c:   a3 38 20 40 00          mov    %eax,0x402038
      401071:   a1 2c 20 40 00          mov    0x40202c,%eax
      401076:   a3 3c 20 40 00          mov    %eax,0x40203c
      40107b:   5d                      pop    %ebp
      40107c:   c3                      ret    
    

    正如您所见,在汇编中赋值操作被简单地替换为“mov”指令,赋值运算符只是意味着将数据从一个内存位置移动到另一个内存位置。 分配只会对结构的直接成员执行此操作,并且当结构中有复杂数据类型时将无法复制。这里的 COMPLEX 意味着你不能有指针数组,指向列表。

    结构中的字符数组本身不适用于大多数编译器,这是因为赋值只会尝试复制,甚至不会查看数据类型是否为复杂类型。

    【讨论】:

    • 你能否详细说明它会失败的条件,因为它似乎总是对我有用
    • 提升这个的唯一原因是这个答案非常具有教育意义。谢谢!
    【解决方案4】:

    这是一个简单的副本,就像您对 memcpy() 所做的一样(事实上,一些编译器实际上会为该代码生成对 memcpy() 的调用)。 C中没有“字符串”,只有指向一堆字符的指针。如果你的源结构包含这样一个指针,那么指针会被复制,而不是字符本身。

    【讨论】:

    • 好的,所以编译器将其转换为 memcpy,请参见此处:godbolt.org/z/nPxqWc - 但是现在如果我传递相同的指针 ab,并且 *a = *b 被转换为memcpy 这是未定义的行为,因为对于 memcpy “内存区域不能重叠。” (引用手册页)。那么编译器在使用memcpy 时是错误的还是我在编写这样的赋值时是错误的?
    【解决方案5】:

    您的意思是“复数”是指具有实部和虚部的复数吗?这似乎不太可能,所以如果不是,您必须举一个例子,因为“复杂”在 C 语言方面没有任何具体含义。

    您将获得结构的直接内存副本;这是否是您想要的取决于结构。例如,如果结构包含一个指针,则两个副本将指向相同的数据。这可能是也可能不是您想要的;这取决于您的程序设计。

    要执行“智能”复制(或“深度”复制),您需要实现一个函数来执行复制。如果结构本身包含指针和结构也包含指针,并且可能是指向此类结构的指针(也许这就是您所说的“复杂”),那么这可能很难实现,并且很难维护。简单的解决方案是使用C++,为每个结构或类实现拷贝构造函数和赋值操作符,然后各自负责自己的拷贝语义,可以使用赋值语法,更容易维护。

    【讨论】:

      【解决方案6】:

      是的,您可以使用简单的赋值语句将结构的一个实例分配给另一个实例。

      • 在非指针或非指针包含结构成员的情况下,赋值意味着复制。

      • 在指针结构成员的情况下,赋值意味着指针将 指向另一个指针的相同地址。

      让我们亲眼看看:

      #include <stdio.h>
      
      struct Test{
          int foo;
          char *bar;
      };
      
      int main(){
          struct Test t1;
          struct Test t2;
          t1.foo = 1;
          t1.bar = malloc(100 * sizeof(char));
          strcpy(t1.bar, "t1 bar value");
          t2.foo = 2;
          t2.bar = malloc(100 * sizeof(char));
          strcpy(t2.bar, "t2 bar value");
          printf("t2 foo and bar before copy: %d %s\n", t2.foo, t2.bar);
          t2 = t1;// <---- ASSIGNMENT
          printf("t2 foo and bar after copy: %d %s\n", t2.foo, t2.bar);
          //The following 3 lines of code demonstrate that foo is deep copied and bar is shallow copied
          strcpy(t1.bar, "t1 bar value changed");
          t1.foo = 3;
          printf("t2 foo and bar after t1 is altered: %d %s\n", t2.foo, t2.bar);
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-06
        • 2012-07-29
        • 2016-02-08
        • 2021-04-04
        • 2013-03-04
        相关资源
        最近更新 更多