ChatGPT解决这个技术问题 Extra ChatGPT

Assign one struct to another in C

Can you assign one instance of a struct to another, like so:

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

I have seen it work for simple structures, bu does it work for complex structures?
How does the compiler know how to copy data items depending on their type, i.e. differentiating between an int and string?


f
fabrizioM

Yes if the structure is of the same type. Think it as a memory copy.


Keep in mind that there's no deep copy, pointed to memory isn't copied.
Concurrency is also an issue here.
@Tim Concurrency is no more an issue than it is for assignment of the built in types, like integers and doubles - assignment is not an atomic operation for these either.
OK, if there is copy created, can I free the memory later with free() ?
@Betlista You can't free the memory with free() because they are automatic variables: en.wikipedia.org/wiki/Automatic_variable
佚名

Yes, assignment is supported for structs. However, there are problems:

struct S {
   char * p;
};

struct S s1, s2;
s1.p = malloc(100);
s2 = s1;

Now the pointers of both structs point to the same block of memory - the compiler does not copy the pointed to data. It is now difficult to know which struct instance owns the data. This is why C++ invented the concept of user-definable assignment operators - you can write specific code to handle this case.


I upped it because reading it made me realise the error/omission in my own answer.
+1 for noting that there is not actually any copying going on.
Why was this flagged as spam? Has someone lost control over their mouse?
@gf And apparently as offensive too!
@rahmanisback The answer of anon is quite clear about this topic: "the compiler does not copy the pointed to data". The data of the struct itself is clearly copied.
J
Jérôme Pouiller

First Look at this example :

The C code for a simple C program is given below

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;
}

The Equivalent ASM Code for foo_assign() is

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    

As you can see that a assignment is simply replaced by a "mov" instruction in assembly, the assignment operator simply means moving data from one memory location to another memory location. The assignment will only do it for immediate members of a structures and will fail to copy when you have Complex datatypes in a structure. Here COMPLEX means that you cant have array of pointers ,pointing to lists.

An array of characters within a structure will itself not work on most compilers, this is because assignment will simply try to copy without even looking at the datatype to be of complex type.


Can u elaborate on which conditions it would fail cause it seems to work for me always
Upped this for the single reason that this answer is just amazingly educational. Thank you!
T
Thomas Pornin

This is a simple copy, just like you would do with memcpy() (indeed, some compilers actually produce a call to memcpy() for that code). There is no "string" in C, only pointers to a bunch a chars. If your source structure contains such a pointer, then the pointer gets copied, not the chars themselves.


OK, so the compiler translates this to memcpy, see here: godbolt.org/z/nPxqWc - But now if I pass identical pointers a and b, and *a = *b is translated to a memcpy that is undefined behaviour, because for memcpy " The memory areas must not overlap." (quoting from the man page). So is the compiler wrong in using memcpy or am I wrong in writing such an assignment?
C
Clifford

Did you mean "Complex" as in complex number with real and imaginary parts? This seems unlikely, so if not you'd have to give an example since "complex" means nothing specific in terms of the C language.

You will get a direct memory copy of the structure; whether that is what you want depends on the structure. For example if the structure contains a pointer, both copies will point to the same data. This may or may not be what you want; that is down to your program design.

To perform a 'smart' copy (or a 'deep' copy), you will need to implement a function to perform the copy. This can be very difficult to achieve if the structure itself contains pointers and structures that also contain pointers, and perhaps pointers to such structures (perhaps that's what you mean by "complex"), and it is hard to maintain. The simple solution is to use C++ and implement copy constructors and assignment operators for each structure or class, then each one becomes responsible for its own copy semantics, you can use assignment syntax, and it is more easily maintained.


D
Dean P

Yes, you can assign one instance of a struct to another using a simple assignment statement.

In the case of non-pointer or non pointer containing struct members, assignment means copy.

In the case of pointer struct members, assignment means pointer will point to the same address of the other pointer.

Let us see this first hand:

#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;
}