为什么没有 return 语句的非 void C 空函数应该复制结构?

Why should a non-void C empty function without return statement copy a struct?

出于某些原因,我想将一个结构深度复制到另一个结构...

根据我编造了下面的例子:

#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>

#define THE_ARRAY_SIZE 10

struct the_struct
{
    uint64_t the_member;
};

typedef struct the_struct the_struct_type;

the_struct_type *the_function(the_struct_type *the_parameter)
{
    the_struct_type *the_returned_variable = NULL;
    *the_returned_variable = *the_parameter;
    return the_returned_variable;
}

void the_show( the_struct_type *the_parameter)
{
    printf( "the_member is %" PRIu64 "\n", the_parameter->the_member);
}

int main(int argc, char *argv[]) {
    
    the_struct_type the_source;
    the_struct_type *the_target = NULL;
    
    the_source.the_member = 7777777;
    
    the_target = the_function(&the_source);
    the_show(the_target); 
}

结果是:

Segmentation fault (core dumped)

出于绝望,我评论了函数的内部结构。就好像有一只看不见的手在指引着我的行动。 :)

the_struct_type *the_function(the_struct_type *the_parameter)
{
    //the_struct_type *the_returned_variable = NULL;
    //*the_returned_variable = *the_parameter;
    //return the_returned_variable;
}

而且,出乎意料的是,结果是我想要的——但不确定是否符合我的预期:

the_member is 7777777

我验证了这种定义更复杂结构的行为,包含数组(但不包含指针)。这种意想不到的行为允许我通过将以下代码添加到 main 来创建结构数组:

int main (int argc, char *argv[]) {
    
    /* */
    
    the_struct_type *the_array_of_struct_type = calloc(THE_ARRAY_SIZE, sizeof *the_array_of_struct_type);
    the_array_of_struct_type[0] = *the_target;
    the_show( &the_array_of_struct_type[0] );

    the_source.the_member = 123456;
    the_target = the_function( &the_source );
    the_show(the_target);
    
    the_array_of_struct_type[1] = *the_target;
    the_show( &the_array_of_struct_type[1] );    
    the_show( &the_array_of_struct_type[0] );
}

导致:

the_member is 7777777
the_member is 7777777
the_member is 123456
the_member is 123456
the_member is 7777777

我理解这是一个约束违规,但结果是这个没有return语句的非void C空函数很深将输入结构复制到输出结构。

我将把它留在这里,以防它对其他人有用。但是这里有更多的东西吗?

我想编译器是这里的关键:我在 Cygwin x86_64 3.0.7(0.338/5/3) 和 Windows 10 下使用 gcc 7.4.0。我用一个简短的:

> gcc code.c -o executable.exe
> gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/7.4.0/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: /cygdrive/i/szsz/tmpp/gcc/gcc-7.4.0-1.x86_64/src/gcc-7.4.0/configure --srcdir=/cygdrive/i/szsz/tmpp/gcc/gcc-7.4.0-1.x86_64/src/gcc-7.4.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libcilkrts --enable-libgomp --enable-libitm --enable-libquadmath --enable-libquadmath-support --disable-libssp --enable-libada --disable-symvers --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
gcc version 7.4.0 (GCC)

The result was: Segmentation fault (core dumped)

这可能是因为您试图取消引用 NULL 指针 the_return_variable 并通过结构的第一个字段分配不存在的指向对象,指针 the_parameter 指向到 the_function.

里面
the_struct_type * the_returned_variable = NULL;
*the_returned_variable = *the_parameter;

取消引用 NULL 指针会调用未定义的行为。

Out of despair I commented the innards of the function. ... And, out of the blue, the result was the desired

结果可能在您执行时符合预期,但行为未定义,或者如 Lundin 所说,它甚至可以被视为 违反约束 使用 return 缺少显式 return 语句的非空函数的值。永远不要相信这种行为。

I understand this is a constraint violation, but the result is that this non-void C empty function without return statement is deep copying the input struct to the output struct.

如上所述,这是未定义的行为/违反约束。你不能相信任何事情。它也不是“复制”结构的内容。

指针parameter的值可能被当作return值,只是赋给了main()中的指针the_target

当您尝试在 the_show 中打印假定复制字段的值时,它实际上只打印原始结构的字段。

Why should a non-void C empty function without return statement copy a struct?

不应该,事实上也没有。 与您示例中的 the_function 完全不同。

void deepCopyPerson (struct person *target, struct person *src)
{
    *target = *src;
}
  1. deepCopyPerson 是一个函数 returning void.
  2. 这里有两个参数(指向目标(目标)和源的指针。
  3. 分配直接从源到目标。

总结:完全不一样


I wanted to deep copy a struct to another struct for reasons...

请注意,如果要复制的结构包含指针成员,则 Peter 方法中所示的赋值将不是 深复制。这将是一个浅拷贝

相关:

如果您想通过值传递 structunion 并将其复制到新的 struct 您无需执行任何操作。

typedef struct 
{
    char name[500];
    double val[40];
    int z[400];
}my_s_t;

my_s_t foo(my_s_t s)
{
    my_s_t s1 = s;

    s1.z[4]++;

    return s1;
}

my_s_t foo1(my_s_t *s)
{
    my_s_t s1 = *s;

    s1.z[4]++;

    return s1;
}

void bar()
{
    volatile my_s_t arr[10];

    /* ......*/

    arr[2] = foo(arr[3]);
    arr[4] = foo1(arr + 5);
}

https://godbolt.org/z/8MWqGf