简体   繁体   English

使用函数参数作为变量

[英]Using function parameter as variable

Is it bad or good practice or maybe undefined behavior to re-assign function parameter inside function? 在函数内部重新分配函数参数是不好的做法还是好的做法,或者是不确定的行为?

Let me explain what I'm trying to do with an example, here the function: 让我用一个例子来说明我要做什么:

void
gkUpdateTransforms(GkNode *node /* other params */) {
  GkNode *nodei;

  if (!(nodei = node->chld))
    return;

  do {
    /* do job */
    nodei = nodei->next;
  } while (nodei);
}

Alternative: 选择:

void
gkUpdateTransforms2(GkNode *node /* other params */) {

  /* node parameter is only used here to get chld, not anywhere else */
  if (!(node = node->chld))
    return;

  do {
    /* do job */
    node = node->next;
  } while (node);
}

I checked assembly output and it seems same, we don't need to declare a variable in second one. 我检查了程序集的输出,看起来好像一样,我们不需要在第二个中声明一个变量。 You may ask what if parameter type changed but same condition would be same for first one, because it also need to be updated. 您可能会问,如果更改了参数类型,但是对于第一个参数,相同的条件将是相同的,因为它也需要更新。

EDIT: Parameters are pass-by-value, and my intention is not edit pointer itself 编辑:参数是按值传递,我的意图不是编辑指针本身

EDIT2: What about recursive functions? EDIT2:递归函数呢? What would happen if gkUpdateTransforms2 was recursive? 如果gkUpdateTransforms2是递归的,会发生什么? I'm confused because function will call itself but I think in every call, parameters will be different stack 我很困惑,因为函数会自行调用,但我认为在每次调用中,参数都会不同

I have no idea why you think this would be undefined behavior - it is not. 我不知道您为什么认为这将是未定义的行为-事实并非如此。 Mostly it is a matter of coding style, there's no obvious right or wrong. 通常,这只是编码风格的问题,没有明显的对与错。

Generally, it is good practice to regard parameters as immutable objects . 通常,优良作法是将参数视为不可变对象 It is useful to preserve an untouched copy of the input to the function. 保留功能的原始输入副本是很有用的。 For that reason, it may be a good idea to use a local variable which is just a copy of the parameter. 因此,最好使用局部变量,该局部变量只是参数的副本。 As you can see, this does not affect performance the slightest - the compiler will optimize the code. 如您所见,这丝毫不影响性能-编译器将优化代码。

However, it is not a big deal if you write to the parameters either. 但是,如果您也写入参数,则没什么大不了的。 This is common practice too. 这也是常见的做法。 Calling it bad practice to do so would be very pedantic. 称其为不良做法将非常令人讨厌。

Some pedantic coding styles make all function parameters const if they shouldn't be modified, but I personally think that's just obfuscation, which makes the code harder to read. 如果不应该修改某些函数式编码样式,则所有函数参数都将变为const ,但我个人认为这只是混淆,这会使代码更难阅读。 In your case such pedantic style would be void gkUpdateTransforms(GkNode*const node) . 在您的情况下,这种学究式样式将为void gkUpdateTransforms(GkNode*const node) Not to be confused with const correctness, which is an universally good thing and not just a style matter. 不要与const正确性相混淆,const正确性是一件普遍的事情,而不仅仅是样式问题。


However, there is something in your code which is definitely considered bad practice, and that is assignment inside conditions. 但是,您的代码中确实存在某些绝对不正确的做法,那就是在条件内部进行赋值。 Avoid this whenever possible, it is dangerous and makes the code harder to read. 尽可能避免这种情况,这很危险,并使代码更难阅读。 Most often there is no benefit. 大多数情况下没有好处。

The danger of mixing up = and == was noted early on in the history of C. To counter this, in the 1980s people came up with brain-damaged things like the "yoda conditions" . 在C的历史早期就注意到混合===的危险。为了解决这一问题,在1980年代,人们想到了诸如“尤达条件”之类的对大脑造成伤害的事物。 Then around 1989 came Borland Turbo C which had a fancy warning feature "possible incorrect assignment". 然后在1989年左右出现了Borland Turbo C,它具有精美的警告功能“可能错误分配”。 That was the death of the Yoda conditions, and compilers since then have warned against assignment in conditions. 那是Yoda条件的死亡,从那时起,编译器就警告不要指定条件。

Make sure that your current compiler gives a warning for this. 确保当前的编译器对此发出警告。 That is, make sure not to use a worse compiler than Borland Turbo from 1989. Yes, there are worse compilers on the market. 也就是说,请确保不要使用比1989年的Borland Turbo更差的编译器。是的,市场上有较差的编译器。

(gcc gives "warning: suggest parentheses around assignment used as truth value") (gcc给出了“警告:建议将用作真值的赋值括号括起来”)


I would write the code as 我将代码写为

void gkUpdateTransforms(GkNode* node /* other params */) 
{
  if(node == NULL)
  {
    return ;
  }

  for(GkNode* i=node->chld; i!=NULL; i=i->next;)
  {
    /* do job */
  }
}

This is mostly stylistic changes to make the code more readable. 这主要是风格上的更改,以使代码更具可读性。 It does not improve performance much. 它不会大大提高性能。

IMHO it is not exactly "bad" practice but it is worthwile to question oneself if there isn't a better way. 恕我直言,这不是完全“坏”的做法,但是值得怀疑的是,没有更好的方法。 About your analyzing the assembler output: it may serve as an interesting and educational look behind the curtain but you are ill advised to use this as an justification for optimization or worse, laziness in the source code. 关于分析汇编程序输出:它可能是有趣而有教育意义的外观,但是不建议您使用它作为优化或更糟的源代码懒惰的理由。 The next compiler or the next architecture may just render your musings completely invalid - my recommendation is to stay with Knuth here: "Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do." 下一个编译器或下一个体系结构可能只会使您的想法完全无效-我的建议是在这里与Knuth保持联系: “与其想象我们的主要任务是指导计算机做什么,不如让我们专注于向人类解释我们想让计算机做什么。” .

In your code I think the decision is 50:50 with no clear winner. 在您的代码中,我认为决定是50:50,没有明确的获胜者。 I would deem the node-iterator a concept of its own, justifying a separate programming construct (which in our case is just a variable) but then again the function is so simple that we don't win much in terms of clarity for the next programmer looking at your code, so we can very well live with the second version. 我认为节点迭代器有一个自己的概念,可以证明一个单独的编程结构(在我们的例子中只是一个变量)是合理的,但是函数又是如此的简单,以至于在下一个方面,我们并没有赢得太多程序员查看您的代码,因此我们可以很好地使用第二个版本。 If your function starts to mutate and grow over time, this premise may become invalid and we were better off the first version. 如果您的功能开始随着时间的推移而变异和增长,则此前提可能会失效,我们最好选择第一个版本。 That said, I would code the first version like this: 也就是说,我会像这样编写第一个版本:

void
gkUpdateTransforms(GkNode *node /* other params */) {    
  for (GkNode *nodei = node->chld; nodei != NULL; nodei = nodei->next) {
      /* do job */
  }
}

This is well defined and a perfectly good way to implement this behaviour. 这是定义明确的,也是实现此行为的完美方法。

The reason you might see it as an issue is the common mistake of doing the following: 您可能将其视为问题的原因是执行以下操作的常见错误:

int func(object a) {
    modify a // only modifying copy, but user expects a to be modified

But in your case, you expect to make a copy of the pointer. 但是对于您的情况,您希望复制该指针。

As long as it's passed by value, it can be safely treated as any other local variable. 只要按值传递它,就可以将其安全地视为任何其他局部变量。 Not a bad practice in this scenario, and not undefined behaviour either. 在这种情况下,这不是一个坏习惯,也不是未定义的行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM