函数参数求值顺序

jen*_*nsa 1 c++ winapi calling-convention argument-passing

我对调用 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"));
Run Code Online (Sandbox Code Playgroud)

现在,最后一个参数是

snprintf(szBuffer, 64, "My text goes here")
Run Code Online (Sandbox Code Playgroud)

它返回写入 char[] szBuffer 的字符数。它还将文本“My text go here”写入 char[] szBuffer。第四个参数是 szBuffer,它包含要写入的文本。但是,我们可以看到 szBuffer 填充在第五个参数中,告诉我们不知何故是表达式

// argument 5
snprintf(szBuffer, 64, "My text goes here")
Run Code Online (Sandbox Code Playgroud)

之前评估过

// argument 4
szBuffer
Run Code Online (Sandbox Code Playgroud)

好的。总是这样吗?评估总是从右到左进行?查看默认调用约定__cdecl

__cdecl 调用约定的主要特点是:

参数从右向左传递,并放置在堆栈中。

堆栈清理由调用者执行。

函数名称通过在其前面加上下划线字符 '_' 来修饰。

(来源:调用约定揭秘)(来源:MSDN on __cdecl

它说“参数从右向左传递,并放在堆栈上”。这是否意味着函数调用中最右边/最后一个参数总是首先被评估?然后倒数第二等等?调用约定__stdcall也是如此,它还指定了从右到左的参数传递顺序。

与此同时,我遇到了这样的帖子:

如何在函数调用中评估参数?

在那篇文章中,答案说(并且他们引用了标准)订单未指定。

最后,当查尔斯·佩佐德(Charles Petzold)写道

TextOut(hdc, x, y, szBuffer, snprintf(szBuffer, 64, "My text goes here"));
Run Code Online (Sandbox Code Playgroud)

也许没关系?因为即使

szBuffer
Run Code Online (Sandbox Code Playgroud)

之前被评估

snprintf(szBuffer, 64, "My text goes here")
Run Code Online (Sandbox Code Playgroud)

函数 TextOut 使用 char*(指向 szBuffer 中的第一个字符)调用,并且由于所有参数在 TextOut 函数执行之前都已计算,因此在这种特殊情况下先计算无关紧要。

cdh*_*wie 5

在这种情况下没有关系

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

回答您提出的问题:未指定参数评估的实际顺序。例如,在声明中f(g(), h()),一个兼容的编译器可以执行g(),并h()以任意顺序。此外,在语句中f(g(h()), i()),编译器可以按照之前执行的约束的任何顺序执行这三个函数g, h,因此它可以执行,然后,然后。ih()g()h()i()g()

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

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