2014-09-23 81 views
1

我必须读出uint32_t变量的字节,并且我已经从我的一位同事那里看到了这种 实现。我的问题是,如果该代码示例的行为在“几乎所有”32位微控制器上是可靠的。是否可以在每个32位微控制器上工作,还是我所依赖的平台特定行为? P.S .:本例中不考虑系统的字节顺序。在嵌入式系统上投射指针,通过指针投射在32位变量上的字节指针

uint8_t  byte0=0; 
uint8_t  byte1=0; 
uint8_t  byte2=0; 
uint8_t  byte3=0; 
uint8_t  *byte_pointer; //byte_pointer 
uint32_t *bridge_pointer;//pointer_bridge between 32bit and 8 bit variable 
uint32_t var=0x00010203; 

bridge_pointer=&var; //bridge_pointer point to var 
byte_pointer=(uint8_t *)(bridge_pointer); //let the byte_pointer point to bridge_pointer 

byte0=*(byte_pointer+0); //saves byte 0 
byte1=*(byte_pointer+1); //saves byte 1 
byte2=*(byte_pointer+2); //saves byte 2 
byte3=*(byte_pointer+3); //saves byte 3 

由于提前

+1

'byte0 = byte_pointer [0]'等会更优雅(相当于'*(byte_pointer + 0)')。另外我不认为'bridge_pointer'是绝对必要的,您可以立即将'&var'强制转换为'uint8_t *'。 – Kninnug 2014-09-23 12:10:27

+1

您可能想了解[别名](http://en.wikipedia.org/wiki/Aliasing_%28computing%29)和[指针别名](http://en.wikipedia.org/wiki/Pointer_aliasing)。 – 2014-09-23 12:11:25

回答

3
byte0=*(byte_pointer+0); //saves byte 0 

这条线(和下面的那些)是违反严格混叠的。声明为uint32_t的对象通过uint8_t类型的左值进行访问;应该使用unsigned char而不是uint8_t,因为允许字符类型的左值访问不同类型的对象(如果存在uint8_t,则尽管放宽了别名规则,它的行为与unsigned char相同)。

unsigned char *byte_pointer = (unsigned char *)(bridge_pointer); 
uint8_t byte0 = *(byte_pointer+0); 
    // byte0 can still be uin8_t, the access to var is important for aliasing 

如在comment提到的,byte_pointer[0]相当于*(byte_pointer+0)和较为常见。

通过此更改,代码具有明确定义的行为。 (并且是便携式的,以具有uint32_tuint8_t实施方式中,虽然端序可导致不同的结果,如在问题指出。)

严格混叠的相关标准部件是6.5 P6/7。

+0

谢谢你的回答,它帮助我很好。其他答案也很有用,谢谢。 – chhegema 2014-09-23 12:55:07

+0

实际上,'uint8_t'的行为与unsigned char完全一样,因此不会破坏严格的别名。 [看到这个](http://stackoverflow.com/questions/12666146/can-uint8-t-be-a-non-character-type)。 – Lundin 2014-09-23 13:50:08

+0

@Lundin:IIRC海湾合作委员会邮件列表讨论了一段时间之前,它被认为是一个海湾合作委员会的错误,它不使用该别名信息,并可能会改变。我不确定目前的状况。当我找到它时我会链接。 – mafso 2014-09-23 14:11:42

4

你应该声明byte_pointerunsigned char*,那么你的例子将工作,如果你接受不同的输出在小端。这里是一个解决方案,它不依赖于字节序

uint8_t byte0 = var; 
uint8_t byte1 = var>>8; 
uint8_t byte2 = var>>16; 
uint8_t byte3 = var>>24; 

byte0将成为LSB

1

在实践中,代码是除了字节序问题,便于携带。通过uint8_t指针访问uint32_t的一部分将始终在标准之外的现实世界中工作。

Whether uint8_t is considered a character type or not is debated,但这个讨论只是在学术上有兴趣。 (如果它被认为是一种字符类型,它不会在标准6.5/7中使用破坏别名规则。)实际上,uint32_t将不包含任何填充位或其他标准允许的理论废话。

为了避免字节码问题,我建议重新编写代码以使用位移,就像在@ mch的答案中进行了反向解析。

+0

“填充位或标准允许的其他此类理论废话。” - 它没有。参看C11(n1520)7.20.1.1 p1/2。 '(u)intN_t'类型表现得非常“正常”:二进制补码和无填充位。 – mafso 2014-09-23 16:17:16

+0

@mafso所以没有理由uint8_t不会工作。 – Lundin 2014-09-24 06:59:49

+0

@Lundin:标准将允许一个实现使用32中的任何一个! (即大约2.63E + 35)的方式将uint32_t的位映射到四个连续的uint8_t值的位。在实践中,两种映射比其他任何映射都要普遍得多,并且在任何非设计实现中可能至多会出现两种映射。 – supercat 2017-10-17 22:46:15