C语言中的位移运算符<<和>>是否会保留进位标志位?

4

我阅读了关于C语言在移位操作中保留进位位的内容,这个信息可以在特定于处理器的.h文件中找到。

这是真的吗?我应该使用这种方法吗?还是我应该自己计算进位位?


也许一些内联汇编可以解决这个问题。 - huseyin tugrul buyukisik
我很好奇您检查进位的用例是什么。当在汇编级别完成时,通常是一种按顺序检查寄存器中位值的技术。在C中有其他方法可以做到这一点,而不涉及检查进位。如果有特定于处理器的实现,则除非您真正需要每次迭代微秒级别的性能调整,否则不会获得太多性能提升。 - lurker
2个回答

8

在C语言中,没有标准的方法来访问原始操作中的进位位。

你需要在一个更大的数据类型中执行移位操作:

uint16_t foo = ...;

uint32_t tmp = (uint32_t)foo << shift;
uint16_t result = (uint16_t)tmp;
uint16_t carry  = (uint16_t)(tmp >> 16);

或者通过执行相反的转移:

uint16_t result = foo << shift;
uint16_t carry  = foo >> (16 - shift);

请注意,如果shift == 0,则此第二种方法将调用未定义的行为。因此,您需要单独处理这种情况。

是的,如果有更大的数据类型可用,那么使用更大的数据类型是最好的选择。请注意:如果“shift <= 0”或“shift >= 16”,答案也会引发未定义的行为。不过,对于任何使用您的精彩答案的人来说,处理这些情况应该不是什么大挑战。 - chux - Reinstate Monica
@chux:谢谢!然而,shift < 0 或 >= 16 无论如何都是未定义行为,而我的第二种方法也意味着 shift == 0 同样是未定义行为。 - Oliver Charlesworth
回顾一下,我认为在第二种方法中移位0可能是可以的。我们都认为uint16_t result = foo << 0没问题吧?但是uint16_t carry = foo >> 16不是已经定义了吗? - chux - Reinstate Monica
先生,您是正确的!(*Ed McMahon) "如果右操作数的值为负或大于等于提升后的左操作数的宽度,则行为未定义。" foo >> 16 是UB。我被困在6.5.7 p4&5中。 - chux - Reinstate Monica

1

标准C不提供对移位操作的进位访问。
一些C实现具有特定于处理器的.h文件或其他扩展,可以允许访问。

在实际应用中,应避免使用来自处理器特定的.h文件或扩展功能。但是如果必须使用,请考虑同时编写C标准解决方案,至少作为文档的一部分。请参见推荐的@Oli Charlesworth解决方案。

通常,要创建有效的可移植代码,可能需要从更高的层面查看问题,并且不使用进位。另一方面,如果这是针对一小范围计算机的,可以使用适合您的(或支付您工资的)方法。

我的早期示例中指出了各种弱点。我现在将其视为依赖于实现。它们已被删除。


你说这个涉及可移植性是正确的。左移负值是未定义行为,与可移植性相去甚远。右移负值是实现定义行为,可能包括符号保留,如果现在不是,那么将来某个时候可能会有。两者都应该避免使用。 - autistic
@ 未定义行为 同意您对我“边缘”示例的评估,并已将其删除。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接