【问题标题】:Should I change the value of a function parameter? [closed]我应该更改函数参数的值吗? [关闭]
【发布时间】:2016-04-04 12:39:58
【问题描述】:

我知道这在C中是允许的,但我习惯不更新值传递的变量的值。

在我的“编码风格”中,按值传递的参数没有改变。 我的意思是我更喜欢这个:

void func(int var)
{
    int locVar = var;

    if(something)
    {
        locVar = locVar/2;
    }

    // [some stuff using locVar]
}

在此:

void func(int var)
{
    if(something)
    {
        var = var/2;
    }

    // [some stuff using var]
}

我假设如果启用了寄存器优化,编译器不会生成不同的程序集,但是,有什么好的理由更喜欢这两个代码 sn-ps 之一吗?

【问题讨论】:

  • 是的,修改参数更容易出错。同样在调试中,您不会知道参数的原始值是什么,而是它的实际值。你不是因为编译器和/或性能而做#1(正确),而是为了你自己。
  • IMO 更改参数是不错的编码风格。这完全取决于您。
  • @Plouff 使用第一种方法的唯一原因是多次使用原始值。否则不需要引入新的局部变量。函数参数已经是它的局部变量了。
  • @AlexanderVaganov 顺便说一句,函数可以有许多参数。你会在函数中通过引入新的局部变量来重命名它们吗?
  • @VladfromMoscow:我倾向于创建我想要更改的所有参数的副本 - 通常很少。

标签: c++ c function parameters


【解决方案1】:

有什么好的理由偏爱两种代码 sn-ps 之一吗?

1) 编译器不是平等的。 int locVar = var; 可以创建更快的代码。 (我很惊讶在给定的应用程序中发现了这一点。)这是本地或微优化,仅在特定情况下有用,当然在使用其他选项或在另一台机器上编译时可能会导致不同的性能。

2) 越少越好。引入int locVar = var; 中的同义词是要理解和维护更多代码和更多变量。 通常这不太有用。

3) 两个代码 sn-ps 都生成有效代码。所以这也是一个风格问题。如果您的编码小组有关于此的编码指南,最好遵循它而不是因为琐碎的原因而有所不同。

选择一个胜过另一个的理由:是的。强烈的理由:一般不会。当不确定该走哪条路时,易于维护的一方胜出 (IMO)。

【讨论】:

  • 总而言之,除非编码指南要求或在极少数情况下作为某些编译器的优化,否则您不会使用额外的变量。我不会认为复制变量会导致可维护性方面的开销。这是一个有趣的观点!
【解决方案2】:

没有。

  • 从计算机的角度来看 - 没有区别。优化将完成这项工作。
  • 个人观点 - 个人喜好问题
  • 从元视图 - 不要设置陷阱

一个函数中经常需要多次初始参数值,因此更常见的样式是不覆盖参数。将这些值用于调试、记录消息(“写入 n 字节”)、catch 子句等非常方便。由于这或多或少是常见的风格,维护者很容易错过您的 itsy-bitsy-premature-optimization。这种优化在非优化 C 编译器时代很常见,现在它们只是“因为我可以”的东西。请记住,我们编写代码以供人类阅读。编译器无论如何都可以做到这一点。

【讨论】:

  • 这些正是我从不修改参数变量的原因。我从来没有想过这会是一个品味问题!
【解决方案3】:

一般来说,引入的变量越多,代码的可读性就越差,代码就越复杂。

有时甚至很难为一个实体发明两个在语义上看起来相同的名称。

例如,一个功能列表可以占据多个屏幕。在这种情况下,如果您遇到像 locVar 这样的名称,您需要向后滚动函数列表以确定该名称的含义。

此外,一个函数可以有多个参数。你要为每个参数引入新的别名吗?

对于您的代码的读者来说,您并不清楚您引入新的局部变量只是为了支持您的编码风格。例如,他们可能认为您更改了函数并忘记删除不再需要的变量。:)

考虑一个递归函数,它计算一个数字的位数之和。

unsigned int sum( unsigned int x )
{
    const unsigned int Base = 10;

    unsigned int digit = x % Base;
    return digit + ( ( x /= Base ) == 0 ? 0 : sum( x ) );
                        ^^^^^^^^^^^^^
} 

或者可以这样写

unsigned int sum( unsigned int x )
{
    const unsigned int Base = 10;

    return x % Base + ( x / Base == 0 ? 0 : sum( x / Base ) );
} 

在这个函数中引入一个新的局部变量作为x的别名的目的是什么?

unsigned int sum( unsigned int x )
{
    const unsigned int Base = 10;

    unsigned int y = x;
    unsigned int digit = y % Base;
    return digit + ( ( y /= Base ) == 0 ? 0 : sum( y ) );
} 

就我而言,没有中间变量y 的第一个函数实现更清楚。当使用相同的变量x 时,函数的递归性质更加明显。

如果您想指出函数中的参数未更改,您可以使用具有限定符 const 的参数声明或定义函数。

例如

unsigned int sum( const unsigned int x )
{
    const unsigned int Base = 10;

    return x % Base + ( x / Base == 0 ? 0 : sum( x / Base ) );
} 

在 C++ 中,函数调用可能看起来像

#include <iostream>

constexpr unsigned int sum( const unsigned int x )
{
    const unsigned int Base = 10;

    return x % Base + ( x / Base == 0 ? 0 : sum( x / Base ) );
} 

int main()
{
    std::cout << sum( 123456789 ) << std::endl;
    int a[sum( 123456789 )];

    std::cout << sizeof( a ) << std::endl;
}    

程序输出是

45
180

【讨论】:

  • 你的第一个例子没有说服力;无论如何它都不会分配给函数参数。
  • @JonathanLeffler 我没明白你说的第一个例子是什么意思?
  • 我的意思是问题是关于通过赋值修改函数参数的值,但您的代码没有分配给函数参数。不过,很难很快找到一个好例子。
  • @JonathanLeffler 谢谢,现在我明白了。我将更新我的示例。
  • @Vincent 是不是 constexpr 函数?:)
猜你喜欢
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
相关资源
最近更新 更多