6

可能重复:
谁能解释这些未定义的行为(i = i++ + ++i,i = i++ 等……)

#include<stdio.h>
#include<conio.h>

#define SQ(x) x*x

void main()
{
   int a1 , a2;
   int b1 , b2;

   a1 = 2;
   a2 = 2;

   b1 = 0;
   b2 = 0;

   b1 = SQ(a1++);
   b2 = SQ(++a2);

   printf("Frist = %d",b1);
   printf("Second = %d",b2);
}

我知道代码的输出是什么。

因为#define 在其他程序中工作,所以它在上面的代码中不起作用为什么。?

4

9 回答 9

16

在同一变量上具有多个 ++ 运算符的表达式的结果在 C中正式是未定义的行为。

于 2010-11-02T15:53:38.007 回答
7

#define是对预处理器的指令,用它的扩展字面替换每个出现的宏。因此,代码中的相关行将传递给编译器:

b1 = a1++ * a1++;
b2 = ++a2 * ++a2;

正如 Seva 所说,这些表达式是官方未定义的;但是,即使我们采用可以说是最明智的阅读方式,您仍然会得到b1 = 2 * 3;and b2 = 3 * 4;(在行后将a1anda2设置为 4。

于 2010-11-02T15:55:24.620 回答
3

SQ 的两种用法都会调用未定义的行为,因为它们在每个序列点多次分配给 a1 和 a2。您不应将具有副作用的表达式(例如赋值或增量)传递给宏。

于 2010-11-02T15:54:39.500 回答
2

恭喜!您刚刚发现为什么将宏用于此类事情是一个坏主意。作为一般规则,只有在无法使用函数完成时才将其设为宏。在这种情况下,SQ可以很容易地实现为一个函数:

int sq (int x)
{
    return x * x;
}
于 2010-11-02T16:04:10.007 回答
2

因为定义只是替换了表达式。所以你得到结果:

b1 = (a1++)*(a1++);
b2 = (++a2)*(++a2;

所以,你得到两次双倍增量。它会导致未定义的行为。

于 2010-11-02T15:54:30.073 回答
1

线条

b1 = SQ(a1++);
b2 = SQ(++a2);

扩大到

b1 = a1++ * a1++;
b2 = ++a2 * ++a2;

它调用未定义的行为(一个对象可能在序列点之间最多修改一次其值),这意味着任何结果都被认为是“正确的”。

这就是这类宏的问题。你真的希望参数被评估一次,但由于扩展它被评估两次。

用这样的表达式调用它也有问题

x = SQ(a + b);

这将扩展到

x = a + b * a + b;

这可能不是你想要的。为了保持运算符优先级,扩展应该被包裹在 中(),例如

#define SQ(x) (x) * (x)
于 2010-11-02T15:57:08.357 回答
1

将宏中的操作数用括号括起来:

#define SQ(X) (X)*(X)

另外,尽量不要在同一个赋值中使用 x++ 和 x 进行操作。可能导致未定义的行为。

干杯!

于 2010-11-02T15:55:16.960 回答
0

查看代码,因为它将被预处理器扩展:

b1 = a1++ * a1++;
b2 = ++a2 * ++a2;

这是未定义的行为,因为您不止一次地修改了一个变量,它们之间没有序列点。

于 2010-11-02T15:55:45.033 回答
0

我真的不知道您的问题是什么,但也许您的预期输出有问题...请注意,#define这是在您的代码编译之前运行的“简单”文本替换。此文本替换不尊重范围或语法正确性。它只是替换与您所说的内容匹配的任何内容。在您的情况下,它不会真正对“a++”的结果进行平方,而是会生成以下代码: b1 = a++ * a++;

如果你看这里:关于 C 编程的问题,你可以看到一些解释为什么“a++ * a++”是官方未定义的行为。

于 2010-11-02T15:58:58.297 回答