首页 > 技术文章 > 使用++需要小心的地方

zhanghonglang 2017-11-10 15:21 原文

参考:必须弄懂的495个c语言问题

先给结论 : 使用自增自减和赋值操作符对同一对象修改两次或修改以后又被引用的的任何组合都可能会出问题。使用自增自减的时候最好单独一个表达式,因为不同的编译器实现方式可能不一样。

 K&R 明智地指出, “如果你不知道它们在不同的机器上如何实现, 这样的无知可能恰恰会有助于保护你。”

1.下面这个++不会翻转secondFlag

static int secondFlag = 0;

...

secondFlag = (secondFlag++) & 0x01; //反复运行这条语句,secondFlag 永远是0

 

2.正确的是

static int secondFlag = 0;

...

++secondFlag;

secondFlag = secondFlag & 0x01; //secondFlag在0,1之间翻转;

 

 

 

2.使用我的编译器,下面的代码 int i=7; printf("%d\n", i++ * i++); 返回 49?不管按什么顺序计算, 难道不该打印出56吗?


尽管后缀自加和后缀自减操作符 ++ -- 在输出其旧值之后才会执行运算,但这里的之后常常被误解。 没有任何保证确保自增或自减会在输出变量原值

后和对表达式的其它部分进行计算之前立即进行。 也不能保证变量的更新会在表达式 完成” (按照 ANSI C 的术语, 在下一个 序列点之前, 参见问题 3.7) 之前

的某个时刻进行。 本例中, 编译器选择使用变量的旧值相乘以后再对二者进行自运算。包含多个不确定的副作用的代码的行为总是被认为未定义。 (简单而言, “

个不确定副作用是指在同一个表达式中使用导致同一对象修改两次或修改以后又被引用的自增, 自减和赋值操作符的任何组合。 这是一个粗略的定义;

 

甚至都不要试图探究这些东西在你的编译器中是如何实现的 (这与许多 C 教科书上的弱智练习正好相反); 正如 K&R 明智地指出, “如果你不知道它们在不同的机器

如何实现, 这样的无知可能恰恰会有助于保护你。"  

 

 3.为什么这样的代码: a[i] = i++; 不能工作?


子表达式 i++ 有一个副作用 —– 它会改变 i 的值 —– 由于 i 在同一表达式的其它地方被引用, 这会导致无定义的结果, 无从判断该引用(左边的 a[i] )是旧值

还是新值。 注意, 尽管在 K&R 中建议这类表达式的行为不确定, C 标准却强烈声明它是无定义的

 

4.对于代码 int i = 3; i = i++; 不同编译器给出不同的结果, 有的为3, 有的为 4, 哪个是正确的?


没有正确答案;这个表达式无定义 同时注意 i++ ++i 都不同于 i+1。 如果你要使 i 自增 1, 使用 i=i+1, i+=1, i++ ++i,
而不是任何组合 。
 

 5.*p++ 自增 p 还是 p 所指向的变量?


后缀 ++ -- 操作符本质上比前缀一目操作的优先级高, 因此 *p++ *(p++) 等价, 它自增 p 并返回 p 自增之前所指向的值。

要自增 p 指向的值, 使用(*p)++, 如果副作用的顺序无关紧要也可以使用 ++*p

 

6.我有一个 char * 型指针正巧指向一些 int 型变量, 我想跳过它们。为什么如下的代码((int *)p)++; 不行?


这是一个转换操作符, 根据定义它只能生成一个右值 (rvalue)。 而右值既不
能赋值, 也不能用 ++ 自增。

要达到你的目的可以用:

 

p = (char *)((int *)p + 1);

或者,因为 p char * , 直接用

p += sizeof(int);

但是, 在可能的情况下, 你还是应该首先选择适当的指针类型, 而不是一味地试图李代桃僵

 

优先级表

 

推荐阅读