【问题标题】:unable to understand how expressions are getting false [duplicate]无法理解表达式如何变得错误[重复]
【发布时间】:2014-06-12 09:14:21
【问题描述】:
int main( ) 
{ 
      int  k = 35 ; 
      printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ; 
} 

上述程序的输出是“0 50 0”,不带引号。我的问题是k==35 和k > 40 怎么是假的?从我的角度来看,k 被分配了 35,所以“k==35”应该是真的;那么k 在 "k=50" 中被赋值为 50,所以 "k > 40" 必须为真,不是吗?

【问题讨论】:

  • 向我们证明“then”部分
  • @pmg:这是未定义的行为,而不仅仅是未指定的执行顺序。
  • @pmg: printf("%d %d\n", 42 + 1, 42 - 1); 是完全定义的行为。你无法判断 43 是在 41 之前还是之后计算的,但这并不重要,因为这两种计算都不影响另一个。 UB 正在修改在整个表达式(printf() 语句)中其他地方使用的变量。使用++k 或k-- 或对k 的任何分配以及在其他地方引用k 都是有问题的。请注意,printf("%d %d\n", 42 + 1, k -= 3); 是已定义的行为; k 的值只使用一次。

标签: c variables assign


【解决方案1】:

printf 调用调用未定义的行为。

函数参数的评估顺序是未指定的行为。这意味着可以从一组所有可能的顺序中以任何顺序评估参数。该标准不要求实施来强制执行任何命令。此外,分隔参数的逗号不是逗号运算符。这意味着参数的评估之间没有序列点。唯一的要求是必须对所有参数进行全面评估,并且必须在调用函数之前发生所有副作用。现在,C99 标准 §6.5 ¶2 说

在前一个序列点和下一个序列点之间,一个对象应该有它的 表达式的评估最多修改一次存储的值。 此外,应仅读取先验值以确定该值 存储起来。

对函数参数的评估是无序的。赋值表达式k = 50 的计算结果为50,但其副作用是将50 赋值给k。如果首先计算此表达式并且其副作用立即发生,则表达式k == 35 将计算为true,k > 40 将计算为false。因此,取决于表达式k = 50 的副作用何时发生,其他两个表达式的计算结果将是true 或false。但是,其他两个表达式违反了标准上述引用部分中的第二点。表达式访问k,但不会更改k 的值。这违反了先验值应只读以确定值 要存储子句。这就是未定义行为的原因。

强调一下,代码中未定义的行为并不是因为参数的求值顺序未指定(不是未定义,这是不同的),而是因为参数的求值之间没有序列点,因此,在此上下文中违反了标准中上述引用的部分。

未定义的行为意味着代码的行为是不可预测的。从程序崩溃到你的硬盘被格式化到恶魔从你的鼻子里飞出来(发射到夸张中),任何事情都可能发生。这只是为了强调标准不要求实现来处理此类情况,您应该始终避免使用此类代码。

进一步阅读 -

【讨论】:

  • 你能解释一下如何指定订单吗?
  • @sam 您不能指定函数参数的评估顺序。实现甚至不需要总是以相同的顺序评估它们。它可以从一组可能的顺序中以任何顺序评估它们。这就是为什么它被称为未指定的行为。你不应该编写依赖于这个顺序的代码。
  • @Deduplicator 参数评估顺序是未指定,但printf 调用调用未定义的行为,因为标准说Between the previous and next sequence point , the prior value of a scalar object that is modified by the evaluation of the expression, shall be accessed only to determine the value to be stored。
  • “这意味着可以从一组所有可能的顺序中以任何顺序评估参数。”那将是花花公子,因为那样会有一个排序约束,因此没有 UB。另外,您是自相矛盾还是排序和排序之间有区别?
  • @ajay 得到了这个概念..赞赏..
【解决方案2】:

由于参数列表中的赋值,您的代码调用了未定义的行为:

k = 50

你大概想要:

printf("%d %d %d\n", k == 35, k == 50, k > 40);

由于您在参数列表中嵌入了赋值,因此任何结果都是允许的。参数没有必需的评估顺序;它们可以按任何顺序进行评估——任何结果都是正确的。为了您自己的理智,永远不要编写依赖于参数评估顺序的代码。

如果您正在学习的书不是简单地强调评估顺序是未定义的,并且任何未定义的行为都是不好的,那么您可能应该丢弃这本书。

【讨论】:

  • 其实是OP打算做的。
  • 我认为问题在我打字的时候变了...
  • 分配k=50 是故意的。实际上我是c新手,这个问题来自“让我们c”一书。它询问该程序的输出,如果程序顺序执行,我不知道k==35 和k>40 是如何为假的?
  • 好的:这是故意的。如果这本书正在讨论未定义的行为,最好解释一下输出是不确定的——并阻止你编写这样的代码。如果不是关于未定义行为的可能性有多严重,那么应该丢弃本书的整个部分,我会担心本书的其余部分。
  • 这本书是Let Us C,是第8版。许多评论是非常免费的;一个不是,我怀疑负面评论背后有充分的理由。
【解决方案3】:

是Undefined Behavior,表示任何事情都可能发生。

为什么是UB?

答案很简单,在k 的写入和k 的读取之间没有排序约束,甚至没有不确定的排序,这不用于确定新的值k.

printf ( "\n%d %d %d", k == 35, k = 50, k > 40 );
// No ordering constraint between function arguments.

另外:如果写入发生在参数列表中调用的函数中(而不是在该函数的参数中),它将是不确定排序,因此不是UB.
不确定排序意味着它在之前或之后排序,而不确定是哪个。它没有UB那么糟糕(随便),但仍然不应该依赖它,因为它的顺序是不可靠的。

int SetVar(int *p, int v) {return *p = v;}

printf ( "\n%d %d %d", k == 35, SetVar(&k, 50), k > 40 );
// No ordering constraint between function arguments, but
// the function call is indeterminately ordered

【讨论】:

  • 你能详细说明一下吗?术语“不确定排序”在 ISO/IEC 9899:2011 §5.1.2.3 Program execution, in ¶3 中定义,但对于初学者来说相当激烈(哎呀;它吓到我了)。
  • Indeterminately sequenced 表示要么在之前要么在之后排序,并没有具体说明是哪个,但是已经排序了。
  • 好的;但你是在想printf("%d %d %d\n", k == 35, abs(k = 50), k > 40);,还是更戏剧化的东西(printf("%d %d %d\n", k == 35, modifier(&k), k > 40); modifier 函数被传递一个指针?
  • 后者,其他在printf参数1、2、4和abs参数之间不加任何约束。这就是为什么我在调用的函数中说的。我应该更加强调吗?
  • 不强调;例子。好吧,这取决于您,但我认为示例比更多解释更清楚。
猜你喜欢
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2018-09-01
  • 2019-03-22
  • 1970-01-01
相关资源
最近更新 更多