我很困惑在调用C ++函数时评估函数参数的顺序。我可能介绍了一些错误的内容,所以请解释一下是否是这种情况。
作为一个例子,Charles Petzold的传奇书籍“Programming Windows”包含如下代码:
// hdc = handle to device context
// x, y = coordinates of where to output text
char szBuffer[64];
TextOut(hdc, x, y, szBuffer, snprintf(szBuffer, 64, "My text goes here"));
现在,最后一个参数是
snprintf(szBuffer, 64, "My text goes here")
返回写入char [] szBuffer的字符数。它还将文本“My text goes here”写入char [] szBuffer。 第四个参数是szBuffer,它包含要写入的文本。但是,我们可以看到szBuffer填充了第五个参数,告诉我们不知何故是表达式
// argument 5
snprintf(szBuffer, 64, "My text goes here")
之前评估
// argument 4
szBuffer
好的,好的。总是这样吗?评估总是从右到左进行?查看默认调用约定 __ cdecl :
__cdecl调用约定的主要特征是:
参数从右向左传递,并放置在堆栈上。
堆栈清理由调用者执行。
函数名称的前缀是下划线字符“_”。
(资料来源:Calling conventions demystified) (来源:MSDN on __cdecl)
它说“参数从右到左传递,并放在堆栈上”。 这是否意味着始终首先评估函数调用中的最右/最后一个参数?然后是倒数第二个?调用约定 __ stdcall 也是如此,它还指定了从右到左的参数传递顺序。
与此同时,我遇到了这样的帖子:
How are arguments evaluated in a function call?
在该帖子中,答案说(并且他们引用了标准)订单未指定。
最后,当Charles Petzold写道时
TextOut(hdc, x, y, szBuffer, snprintf(szBuffer, 64, "My text goes here"));
也许没关系?因为即使
szBuffer
在之前评估
snprintf(szBuffer, 64, "My text goes here")
使用char *(指向szBuffer中的第一个字符)调用函数TextOut,并且因为在TextOut函数进行之前评估所有参数,所以在这个特殊情况下首先评估它是无关紧要的。
答案 0 :(得分:4)
在这种情况下无关紧要。
通过将szBuffer
传递给接受char *
(或char const *
)参数的函数,数组会衰减为指针。指针值与存储在数组中的实际数据无关,无论是TextOut()
的第四个或第五个参数是否先得到完全评估,指针值在两种情况下都是相同的 。即使第一个参数首先被完全评估,它也会被评估为指向数据的指针 - 指向数据是被改变的,而不是指针本身。
回答你提出的问题:论证评估的实际顺序是未指定的。例如,在语句f(g(), h())
中,兼容的编译器可以按任何顺序执行g()
和h()
。此外,在语句f(g(h()), i())
中,编译器可以以任何顺序执行三个函数g
,h
和i
,并执行h()
的约束在g()
之前 - 所以它可以执行h()
,然后i()
,然后g()
。
恰巧在这种特定情况下,参数的评估顺序完全无关紧要。
(这种行为都不依赖于调用约定,它仅处理参数如何传递给被调用函数。调用约定不会以任何方式解决这些参数的计算顺序。)
答案 1 :(得分:-1)
我同意这取决于调用约定,因为标准没有指定顺序。 另见:Compilers and argument order of evaluation in C++
我也同意在这种情况下无关紧要,因为snprintf总是在TextOut之前进行评估 - 并且缓冲区被填充。