【问题标题】:Function argument evaluation order [duplicate]函数参数评估顺序[重复]
【发布时间】:2015-07-13 19:48:08
【问题描述】:

我对调用 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 的字符数。它还将文本“我的文本放在此处”写入 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?

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

最后,查尔斯·佩佐德写道

TextOut(hdc, x, y, szBuffer, snprintf(szBuffer, 64, "My text goes here"));

也许没关系?因为即使

szBuffer

在之前评估

snprintf(szBuffer, 64, "My text goes here")

函数 TextOut 使用 char* 调用(指向 szBuffer 中的第一个字符),并且由于在 TextOut 函数继续执行之前评估所有参数,因此在这种特殊情况下首先评估它并不重要。

【问题讨论】:

  • C 和 C++ 是不同的语言。如果您有兴趣获得两者的答案,您应该在问题中明确提及它们。就目前而言,您只是在询问 C++。
  • “参数从右到左传递,并入栈”:想想“传递”和“求值”的区别。您可能想阅读唯一的权威来源:standard - 好吧,它是最终草案,但这没有什么区别。一个好的 serach-term 将是 sequence points
  • 如你所说,在这种情况下没关系,因为参数 szBuffer 只是一个指针。即使稍后将评估 snprintf 表达式,此指针也不会更改。只是 szBuffer 指向的缓冲区确实发生了变化。
  • 感谢您的 cmets,我明白我的困惑归结为区分“通过”和“评估”。评估顺序:未指定,参数传递顺序:从右到左。
  • @jensa 参数传递顺序不一定从右到左。在带有 cdecl 的 32 位 Windows 上是的。在 64 位 Windows 上,调用约定是基于寄存器的约定。

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


【解决方案1】:

在这种情况下没关系

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

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

碰巧在这种特定情况下,参数的评估顺序完全不相关。

(这些行为都不依赖于调用约定,它只处理参数如何与被调用函数通信。调用约定不以任何方式解决这些参数的评估顺序。)

【讨论】:

  • 谢谢你,一个非常有帮助的答案!他们描述 char* 的方式正是我认为的方式,这无关紧要。
【解决方案2】:

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

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

【讨论】:

  • 不,它不依赖于调用约定。未指定参数评估的顺序。 -1
猜你喜欢
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
  • 2020-02-15
相关资源
最近更新 更多