为啥我要在 C 中按值传递函数参数?

Posted

技术标签:

【中文标题】为啥我要在 C 中按值传递函数参数?【英文标题】:Why would I pass function parameters by value in C?为什么我要在 C 中按值传递函数参数? 【发布时间】:2012-12-18 11:13:19 【问题描述】:

在我的一些 C 库上工作时,我正在磨练我的 C 技能。在完成了第一个工作实现之后,我现在正在检查代码以使其更高效。目前我的主题是通过引用或值传递函数参数。

我的问题是,为什么我会在 C 中按值传递任何函数参数?代码可能看起来更干净,但它不会总是比通过引用传递效率低吗?

【问题讨论】:

每个参数在 C 中按值传递。如果它不大于指针,传递副本的效率肯定不会低。 更不用说会有多不方便了:int get_int(); void process_int_A(const int x); void process_int_B(const int* const x);。比较:void foo() process_int_A(get_int()); void foo() const int result = get_int(); process_int_A(&result); 。需要为每个返回值显式的中间存储将是致命的。在 C++ 中这不是什么问题,在 C++ 中,你有可以绑定到临时值的“真实”引用。 【参考方案1】:

在 C 中,所有参数都按值传递。真正的通过引用传递是当您看到修改的效果时根本没有任何显式间接:

void f(int c, int *p) 
  c++; // in C you can't change the original paramenter passed like this
  p++; // or this

使用值而不是指针通常是可取的:

int sum(int a, int b) 
    return a + b;

你不会这样写:

int sum(int *a, int *b) 
    return *a + *b;

因为它不安全而且效率低下。效率低下,因为有一个额外的间接。此外,在 C 中,指针参数向调用者建议将通过指针修改值(尤其是当指向的类型的大小小于或等于指针本身时更是如此)。

【讨论】:

这可能是一个愚蠢的问题,但为什么int sum(int *a, int *b) return *a + *b; 函数不安全? 因为那样你就有间接 NULL/悬空指针的风险,如果没有指针,这种情况永远不会发生。 调用者现在需要为a和b创建一个变量,不能传递常量。调用者还必须决定他们是否需要malloc 值或传递变量的地址,并假设值可能被修改,因为它们不是const(可能需要复制值)。如果调用者传递NULL、未初始化的指针和/或指向无效内存位置的指针,那么取消引用指针也是不安全的。【参考方案2】:

请参考Passing by reference in C。通过引用传递在 C 中是用词不当。它是指传递变量的地址而不是变量,但是您传递的是指向变量的指针按值

也就是说,如果您将变量作为指针传递,那么是的,它会稍微更有效,但主要原因是能够修改它指向的原始变量。如果您不想这样做,建议您按价值衡量以明确您的意图。

当然,就 Cs 较重的数据结构之一而言,所有这些都没有实际意义。不管你喜不喜欢,数组都是通过一个指向它们的第一个变量的指针来传递的。

【讨论】:

感谢所有的信息【参考方案3】:

两个原因:

    通常,您必须取消对多次传入的指针的引用(想想长 for 循环)。您不想在每次要查找该地址的值时都取消引用。直接访问速度更快。

    有时你想在你的函数中修改传入的值,而不是在调用者中。示例:

    void foo( int count )
        while (count>0)
            printf("%d\n",count);
            count--;
        
    
    

如果您想使用通过引用传递的内容来执行上述操作,您将不得不在您的函数中创建另一个变量来首先存储它。

【讨论】:

取消引用变量与直接访问的性能损失是什么?它是在数量级的土地上吗? 可以是 3 个周期而不是 1 个周期。如果您正在为四核机器编程,谁在乎(主要)?如果您正在构建任何基于性能的东西(实时应用程序、嵌入式系统),这可能会产生巨大的影响。 个人资料,个人资料,个人资料!此答案的第 1 部分极具推测性,并未考虑编译器优化。【参考方案4】:

因为为计算机编写代码不如为下一个人类编写代码重要。如果您正在传递引用,那么任何读者都必须假设任何被调用的函数都可以更改其参数的值,并且有义务在调用之前对其进行检查或复制参数。

你的函数签名是一个契约,它把你的代码分开,这样你就不必为了理解某个区域发生的事情而将整个代码库放入你的头脑中,通过传递你正在做下一个的引用一个人的生活更糟,作为程序员,你最大的工作应该是让下一个人的生活变得更好——因为下一个人很可能就是你。

【讨论】:

好答案。此外,按值传递实际上可以更快,因为(除其他外)编译器知道参数不会相互别名,因此可能会生成更好的代码。 “让下一个人的生活变得更糟”的例子是一个很好的例子,但我忽略了这一点。 而且我一直认为下一个人是一个知道你地址的杀人狂。

以上是关于为啥我要在 C 中按值传递函数参数?的主要内容,如果未能解决你的问题,请参考以下文章

在lisp中按值传递参数

Pycuda 在函数参数中按值声明数组时返回错误

Pycuda 在函数参数中按值声明数组时返回错误

为啥对按值传递的参数使用 const? [复制]

VB 参数传递:按值传递和按地址传递

为啥要在 C 中声明一个只包含数组的结构?