在函数内重新分配函数参数是不好还是好的做法还是未定义的行为?
让我解释一下我试图用一个例子来做什么,这里是函数:
void
gkUpdateTransforms(GkNode *node /* other params */) {
GkNode *nodei;
if (!(nodei = node->chld))
return;
do {
/* do job */
nodei = nodei->next;
} while (nodei);
}
替代:
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);
}
我检查了程序集输出,看起来一样,我们不需要在第二个声明变量。您可能会问,如果参数类型发生了变化,但第一个参数类型的条件相同,因为它也需要更新。
编辑:参数是按值传递的,我的意图不是编辑指针本身
EDIT2:递归函数怎么样?如果gkUpdateTransforms2是递归的,会发生什么?我很困惑,因为函数会调用自己,但我认为在每个调用中,参数都是不同的堆栈
答案 0 :(得分:4)
我不知道为什么你认为这将是未定义的行为 - 事实并非如此。主要是编码风格问题,没有明显的正确与错误。
通常,将参数视为immutable objects是一种好习惯。保留函数输入的未触摸副本很有用。因此,使用仅仅是参数副本的局部变量可能是个好主意。正如您所看到的,这不会影响性能 - 编译器将优化代码。
但是,如果你写参数也没什么大不了的。这也是常见的做法。称这样做不好的做法会非常迂腐。
如果不修改所有函数参数const
,一些迂腐编码样式会使所有函数参数void gkUpdateTransforms(GkNode*const node)
,但我个人认为这只是混淆,这使代码更难阅读。在你的情况下,这种迂腐的风格将是=
。不要与const正确性混淆,这是一种普遍的好事,而不仅仅是一种风格问题。
然而,你的代码中有一些东西肯定被认为是不好的做法,那就是内部条件的赋值。尽可能避免这种情况,这很危险并且使代码更难阅读。大多数情况下没有任何好处。
在C的历史早期就注意到混淆了==
和void gkUpdateTransforms(GkNode* node /* other params */)
{
if(node == NULL)
{
return ;
}
for(GkNode* i=node->chld; i!=NULL; i=i->next;)
{
/* do job */
}
}
的危险。为了解决这个问题,在20世纪80年代,人们想出了像{{3}这样的大脑受损的东西。 }。然后在1989年左右出现了Borland Turbo C,它有一个花哨的警告功能"可能不正确的任务"。这就是Yoda条件的死亡,自那时起编制者就已经警告不要在条件下进行任务。
确保您当前的编译器发出警告。也就是说,确保不要使用比1989年更糟糕的编译器而不是Borland Turbo。是的,市场上有更糟糕的编译器。
(gcc给出"警告:建议用作真值的约定括号")
我会将代码编写为
div
这主要是风格上的变化,使代码更具可读性。它不会提高性能。
答案 1 :(得分:2)
恕我直言,这不完全是"坏"练习,但如果没有更好的方法,就要质疑自己。关于你对汇编程序输出的分析:它可以作为一个有趣和教育背景幕后但你不建议使用它作为优化的理由或更糟糕的源代码中的懒惰。下一个编译器或下一个架构可能只会使你的思考完全无效 - 我的建议是留在Knuth这里:"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."。
在你的代码中,我认为决定是50:50而没有明确的赢家。我认为node-iterator是一个自己的概念,证明了一个单独的编程结构(在我们的例子中只是一个变量)但是这个函数再次非常简单,我们在清晰度方面不会赢得太多下一个程序员正在查看你的代码,所以我们可以很好地使用第二个版本。如果你的函数开始变异并且随着时间的推移而增长,那么这个前提可能会变得无效,而我们的第一个版本就更好了。 也就是说,我会像这样编写第一个版本:
void
gkUpdateTransforms(GkNode *node /* other params */) {
for (GkNode *nodei = node->chld; nodei != NULL; nodei = nodei->next) {
/* do job */
}
}
答案 2 :(得分:1)
这是明确定义的,也是实现此行为的绝佳方式。
您可能将其视为问题的原因是执行以下操作时常见的错误:
int func(object a) {
modify a // only modifying copy, but user expects a to be modified
但在你的情况下,你希望复制指针。
答案 3 :(得分:1)
只要它按值传递,就可以安全地将其视为任何其他局部变量。在这种情况下并非坏习惯,也不是未定义的行为。