让我们考虑两个例子
1:8位MCU / MPU /平台-小端
uint8_t arr[5] = {0x1,0x2,0x3,0x4,0x5};//assume &arr[0] == 0x0
uint32_t *ui32 = (uint32_t*)&arr[1];
*ui32
的值是什么? 0x2030405
?
在该平台上是否需要将uint32_t
变量放置到4的地址倍数?
1:32位MCU / MPU /平台-小尾数
几乎相同的示例:
uint8_t arr[] = {0x1,0x2,0x3,0x4,0x5, 0x6, 0x7, 0x8}; //again assume &arr[0] == 0x0
uint32_t *ui32 = (uint32_t*)&arr[1];
*ui32
的值是什么?
我知道32位变量应该位于4的地址倍数中。
在哪里可以找到相关规范?
答案 0 :(得分:2)
您的代码包含未定义的行为,并且不可移植。例如,在我编写过的某些UNIX工作站上,内存访问必须与操作数的大小对齐,因此大多数(并非所有)时间,尝试取消引用(uint32_t*)&arr[1]
会导致{{1 }},由内存总线引起的硬件错误。编译器使您可以像这样用脚射击自己。像您一样强制转换指针违反了C的严格别名规则,这会导致未定义行为。
您可以通过编写标准明确允许的SIGBUS
来解决此问题。从现在开始,我将假设您正在做与此等效的操作。如果未在数组中使用偏移量,则还可以在C中用union的字段键入= pun(尽管在C ++中规则不同)。
按照标准,数组的元素必须连续存储,并且它们之间没有填充。在某个对象uint32_t x; memcpy( &x, &array[1], sizeof(x) )
与memcpy()
数组之间的x
是合法的,其结果称为对象表示形式。
使用unsigned char[sizeof(x)]
将任意位复制到<stdint.h>
中任何精确宽度类型的对象表示中是未指定行为,不是未定义行为 >。这是一个格式正确的程序,即使语言标准没有说明必须写的内容,您也会从其中得到一些有效的memcpy()
。您没有授予编译器执行任何所需操作的权限,例如Kill All Humans。这仅是因为该标准不允许精确宽度整数类型具有值位以外的任何其他位,因此,它们不能具有 trap表示形式,无效的位模式,如果将这些位模式复制到该位中,则会导致未定义的行为该类型的值。 (标准中的示例是在每个单词中存储一个奇偶校验位的实现。)
但是,这种保证的另一面是,不能保证类型uint32_t
和uint8_t
存在,并且在现实世界中有一些体系结构可以使它们的符合版本永远不存在。 (不过,保证uint32_t
可以正常工作。)
可以在代码上正确运行的现实世界中的低端实现可能会告诉您unsigned char array[sizeof(uint_least32_t) + 1]
是*u32
。否则,我们将其称为little-endian以外的东西。但是,某些编译器让程序员有责任严格遵循严格的锯齿规则。众所周知,它们会产生优化的代码,如果您通过任一指针编写代码,它们都无法达到您的期望。
答案 1 :(得分:2)
1:8位MCU / MPU /平台-小端
uint8_t arr[5] = {0x1,0x2,0x3,0x4,0x5};//assume &arr[0] == 0x0 uint32_t *ui32 = (uint32_t*)&arr[1];
*ui32
的值是什么?
C明确声明在这种情况下读取*ui32
的值的作用是不确定的,这是因为通过不同类型的左值读取对象(arr
的一部分)的值
0x2030405
?
绝对不能保证,但实际上并不罕见,通过读取*ui32
获得的值将是将包含arr
的元素1-4的位模式解释为uint32_t
,但未指定代表的数字。由实现方式决定如何将物理字节映射到逻辑字节。
但是,如果使用“ little-endian”,则表示C实现的uint32_t
由最低有效位到最高有效位的4-8位字节序列表示,并且如果您认为对指针的取消引用确实确实将指向的位模式解释为uint32_t
的指针,因此结果值将与整数常量0x05040302u
表示的值相同。
是否有必要
uint32_t
变量要在这个平台上放置为4的地址倍数?
您尚未指定平台,甚至没有指定狭窄的 class 平台。我通常希望8位平台不需要为uint32_t
类型的对象进行4字节对齐,但是C没有指定,并且平台和实现可能会有所不同。
1:32位MCU / MPU /平台-小端
几乎相同的示例:
答案完全相同,只是类型uint32_t
的对象更有可能(但不确定)是4字节对齐。
我知道32位变量应该位于4的地址倍数中。
不一定。确实某些32位平台确实需要它。有些不需要它,但是可以更快地访问对齐的对象;有些根本不在乎。
在哪里可以找到相关规范?
您可能会感兴趣的C实现的此类详细信息可以在该实现的文档中找到。基础系统的ABI和/或硬件文档可以用作辅助资源。
总的来说,最好的建议通常是完全避免此类问题。避免未指定的,未定义的,特别是未定义的行为,将使您完全依赖C标准来预测程序的行为。
答案 2 :(得分:0)
8位MCU / MPU /平台-小端
答案将假定该平台以某种方式支持更长的整数(即使CPU可能不支持),并且它们是低位优先的。
请注意,如果uC确实是8位的,并且没有更长整数的概念,那么谈论其(字节)字节序就没有多大意义。例如,我们可以说它既是小端又大端的(或者不是全部)。
//assume &arr[0] == 0x0
这可能暗示这是来自一些有关未对齐访问的练习。
*ui32
的值是什么?0x2030405
?在该平台上是否需要将uint32_t
变量放置到4的地址倍数?
这取决于平台和编译器的选项(例如,如果编译器采用严格的别名,那么从一开始就是未定义的行为)。
但是,由于这是一个8位平台(并且假设您告诉编译器执行您似乎想做的事情),因此可以合理地猜测,uint32_t
必须在软件中受支持并且未对齐访问不是问题。假设该软件实现将整数作为低位字节序存储在内存中(如上所述),那么可以,0x05040302
是一个很好的猜测。
32位MCU / MPU /平台-小字节序
*ui32
的值是什么?
同样,在这种情况下,它取决于平台/编译器。在其中一些中,甚至没有任何值,因为当您尝试读取这样的地址时,CPU会陷井(因为&arr[0] == 0
,ui32 == 1
不能与例如4对齐)。>
我知道32位变量应该位于4的地址倍数中。
通常,但取决于平台。另外,即使平台支持不对齐访问,也可能比对齐访问要慢(因此无论如何都希望它们对齐)。
在哪里可以找到相关规范?
除了C规范之外,您还需要查看编译器的文档和体系结构的手册。