我有一个16位无符号变量。我需要将它分成8位块。
正在做以下事情:
chunk_lsb = (uint8)variable;
chunk_msb = (uint8)(variable >> 8);
或者我应该使用面具:
chunk_lsb = (uint8)(variable & 0xFFu);
chunk_msb = (uint8)((variable >> 8) & 0xFFu);
我知道这两种方法都有效,我只是在寻找最好的方法,如果有的话。也许没有,只是使用演员来减少计算是最好的方法?你们有什么感想?
目前尚不清楚variable
是什么类型的。如果没有指定,我们只能推测。
但一般来说,你应该避免在有符号整数类型上进行位移,因为这会导致各种形式的定义不明确的行为。这反过来意味着你必须小心小整数类型,因为它们被提升为签名的int
。见Implicit type promotion rules。
如果(uint8)((variable >> 8) & 0xFFu);
未签名,variable
的具体情况是安全的。否则它是不安全的,因为右移负值会导致实现定义的行为(算术或逻辑移位)。
如果variable << 8
是一个小整数类型或variable
,int16_t
将在16位系统上调用未定义的行为。
因此,无论左/右移动,最安全,最便携的方式是:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
虽然您可能希望过于明确,以便使所有编译器警告静音:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
由于uint8
未签名,因此您无需进行屏蔽:
6.3.1.3 Signed and unsigned integers
- 当具有整数类型的值转换为除_ Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
- 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。 60)
- 否则,新类型将被签名,并且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。
但是,很可能两者都会产生相同的编译器输出。我通常添加掩码,因为它清楚地说明了应该做什么代码,并且不需要进行强制转换。
是否应该使用强制转换来截断长变量?
如果chunk_lsb
是一个8位对象(比variable
窄),使用cast或mask(不是两者)。有助于平息有关减少范围的迂腐警告。我更喜欢掩码 - 除非编译器挑剔。
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
否则使用面具。
unsigned chunk_lsb = variable & 0xFFu;
也许没有,只是使用演员来减少计算是最好的方法?
一般来说,asm代码是相同的,所以就速度而言,使用哪一个并不重要:
你们有什么感想?
IMO,第一个在可读性方面更清晰,但我无法找出支持我偏好的编码标准或指南。无论如何,如果你的偏好是第二个,我会使用const
变量,去除幻数并更清楚地说明目的是掩盖(假设你为const变量选择了正确的名称)。