函数参数评估顺序

时间:2015-07-13 19:48:09

标签: c++ winapi argument-passing calling-convention

我很困惑在调用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函数进行之前评估所有参数,所以在这个特殊情况下首先评估它是无关紧要的。

2 个答案:

答案 0 :(得分:4)

在这种情况下无关紧要

通过将szBuffer传递给接受char *(或char const *)参数的函数,数组会衰减为指针。指针值与存储在数组中的实际数据无关,无论是TextOut()的第四个或第五个参数是否先得到完全评估,指针值在两种情况下都是相同的 。即使第一个参数首先被完全评估,它也会被评估为指向数据的指针 - 指向数据是被改变的,而不是指针本身。

回答你提出的问题:论证评估的实际顺序是未指定的。例如,在语句f(g(), h())中,兼容的编译器可以按任何顺序执行g()h()。此外,在语句f(g(h()), i())中,编译器可以以任何顺序执行三个函数ghi,并执行h()的约束在g()之前 - 所以它可以执行h(),然后i(),然后g()

恰巧在这种特定情况下,参数的评估顺序完全无关紧要。

(这种行为都不依赖于调用约定,它仅处理参数如何传递给被调用函数。调用约定不会以任何方式解决这些参数的计算顺序。)

答案 1 :(得分:-1)

我同意这取决于调用约定,因为标准没有指定顺序。 另见:Compilers and argument order of evaluation in C++

我也同意在这种情况下无关紧要,因为snprintf总是在TextOut之前进行评估 - 并且缓冲区被填充。