在C语言中将结构体传递给函数

6
如果我有以下内容:-
struct foo
{
  int a;
  int *b;
} bar;

void baz(struct foo qux)
{

}

我是否正确地认为将 bar 传递给 baz() 导致将 bar 的局部副本推入堆栈?如果是这样,那么这是什么类型的副本?在C++中,我假设它会调用复制构造函数或默认复制构造函数,但我不知道这在C语言中如何工作。

C语言有默认复制构造函数的概念吗?这个构造函数有名称吗?可以通过某种方法执行深度复制吗?(假设)我唯一想到的方法是先进行深度复制,然后再将其传递给函数。

通常情况下,我会传递一个指向 foo 的指针,但我只是好奇它是如何工作的。此外,我认为传递指针更快、节省内存,并且是执行此类操作时推荐采取的行动。我猜它是浅复制;这可以改变吗?

2个回答

4
我猜传递 bar 给 baz() 会导致将其本地副本推入堆栈?
是的。
我不知道在 C 中这该如何实现。
与 C++ 中的默认复制构造函数差不多;每个复制的字段都会用相应原始字段初始化。当然,由于 "as if" 规则,整个过程可能归结为 memcpy。
我认为传递指针更快,节省内存,并且在执行此类操作时建议采取此方法。
对于较大的结构体,通常是这样,但并不总是如此;如果您有一个包含少量小字段的小结构体,则复制的开销可能比间接寻址的开销要小(此外,使用指针参数可能很昂贵,因为 C 和 C++ 的别名规则可能会防止某些优化)。
我猜这是浅拷贝;可以更改吗?
不行,默认复制构造函数会进行浅拷贝(盲目复制每个字段),而使用 "深拷贝" 通常还会创建指针/引用字段中所引用的每个对象的副本。
你所说的是“按引用传递”,这不是默认设置,以允许最大的灵活性(并与传递基本类型的方式保持一致)。如果要按引用传递,可以传递指针(或在 C++ 中传递引用),通常为 const,如果您只是为了性能,则传递对象本身。

注意,在 C 语言中并没有构造函数,只有在 C++ 中才有。在 C 语言中,你的数据结构将会被逐字节地复制。 - Edward Falk
@EdwardFalk:当然,实际上我谈论的是初始化,而不是构造;此外,“逐字节”这个东西并没有被标准指定,标准实际上只保证命名字段被复制(“即使在初始化之后,结构对象的未命名成员的值也是不确定的。”,C99 §6.7.8 ¶9)- 也就是说,填充可能会被复制,也可能不会被复制。 - Matteo Italia
非常好的解释,我猜您不能覆盖默认行为以执行深复制,如果需要的话? - chrisw
1
@chrisw69:在C++中,您可以通过提供自定义复制构造函数来实现;例如,在标准库容器(std::vector,...)中经常这样做。而在C语言中,则没有自动机制,您必须提供自己的函数(手动调用)来执行深度复制。顺便说一句,对于某些人来说,这被视为优点,因为某些对象的“默认深度复制”策略,如果不理解得好,可能会导致相当大的减速。与Java或C#进行比较,其中默认情况下通过引用传递对象,但可以选择提供Clone方法来执行深度复制。 - Matteo Italia
我相信在C#和Java中,对象引用是按值传递的;ref关键字通过引用传递对象,据我所知,这与在C或C++等语言中传递指向指针的指针是相同的。至于Clone,我认为ICloneable接口不建议使用,因为它没有指定克隆是深层还是浅层的。https://dev59.com/CXRB5IYBdhLWcg3wuZY1 - chrisw
@chrisw69:关于引用的问题:按值传递对象引用与按引用传递对象是一样的,只是术语上的区别(因为在C#/Java中,您只有对象引用,没有“显式”指针);至于ICloneable,你是对的,这与C ++复制构造函数/赋值运算符的问题相同(深/浅语义可能因类而异)。 - Matteo Italia

1

是的,bar的本地副本被推送到堆栈上。其余部分在下面的工作示例中有注释。

    #include <stdio.h>
    struct foo
    {
        int a;
        int *b;
    } bar;
    void baz(struct foo qux)
    {
        bar.a = 2; // if its a different copy then printf on main should print 1 not 2.
        *bar.b = 5; // if its a different copy then printf on main should print 5 not 4. since the place pointer pointing to is same
    }
    int main(){
        bar.a=1;
        bar.b = (int*)malloc(sizeof(int));
        *bar.b = 4;
        baz(bar); // pass it to baz(). now the copy of bar in the stack which is what baz going to use
        printf("a:%d | b:%d\n",bar.a,*bar.b);
        //answer is  2 and 5
        /*So indeed it does a shallow copy thats why we lost the "4" stored in bar.b it did not created new space in heap to store "5" instead it used the same space that b was pointing to.
        */
    return 0;
    }

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