我按如下方式填写内存:
char buf[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
然后将无符号长指针依次放在前5个字节并输出结果:
char *c_ptr;
unsigned long *u_ptr;
c_ptr = buf;
for (int i=0;i<5;i++)
{
u_ptr = (unsigned long *)c_ptr;
printf("%X\n",*u_ptr);
c_ptr++;
}
当我在x64平台上执行此代码时,我得到了我的预期:
44332211
55443322
66554433
77665544
88776655
但是当我在ARM平台上执行相同的代码时,我得到以下内容:
44332211
11443322
22114433
33221144
88776655
即。它每4个字节绑定一次,并在此范围内仅取消引用4个字节。
所以我想问一下,如果这种行为(当pointer_value%4 != 0
时)是错误的还是特定于实现的?
UPD: 我知道关于endiannes,我想知道这是正确的,我得到了
11443322
而不是
55443322
当我有例如0x10000001
的指针时
它使用地址0x10000001
,0x10000002
,0x10000003
和0x10000000
而不是0x10000005
的字节进行无符号长整数。
答案 0 :(得分:3)
在怀疑内存对齐后,我做了一个快速的google =)
http://awayitworks.blogspot.co.nz/2010/02/arm-memory-alignment.html
在该文章中说明:
直到ARMv4架构,它假定为获取提供的地址 内容是内存对齐的...... 32位数据获取应该有地址 对齐到32位等等。正确猜测问题只是 用于32位和16位数据获取。 ARM忽略较低的2位 如果数据提取是32位,则地址;如果是数据,则忽略低1位 获取是16位。所以,总之如果地址没有正确对齐 然后数据获取将是错误的。
注意最后一句=)
如果您需要在x86上预期的行为,则必须从字符显式构建整数,即(假设为little-endian):
// Endian-specific
inline unsigned long ulong_at( const char *p ) {
return ((unsigned long)p[0])
| (((unsigned long)p[1]) << 8)
| (((unsigned long)p[2]) << 16)
| (((unsigned long)p[3]) << 24);
}
或者也许:
// Architecture-specific
inline unsigned long ulong_at( const char *p ) {
unsigned long val;
char *v = (char*)&val;
v[0] = p[0];
v[1] = p[1];
v[2] = p[2];
v[3] = p[3];
return val;
}
答案 1 :(得分:2)
如果你想从内存中取一个四字节字,地址应该是四的倍数。
在任何架构上,错位访问通常都是一个坏主意。有些人会抛出一个SEGFAULT,其他人会透明地处理错误,并且 - 非常慢 - 通过获取包含所需值的两个单词并将它们拼接在一起来合成正确的值。看来(虽然我不是专家)ARM正在获取指针占用的四字节插槽并旋转结果,以便寄存器的LSB与指针匹配。
答案 2 :(得分:1)
Endianness不解释这种行为。似乎ARM处理器不允许四字节内存访问未对齐四字节边界,并且输出指示处理器读取内存,就好像它每个字节访问了rightward bitwise rotation 8位一样超过四字节边界。有关内存对齐的更多信息,请参阅this Wikipedia article。
事实上,在某些体系结构上,如果您尝试执行此类未对齐的内存访问,则可能会出现bus error。
答案 3 :(得分:1)
问题在于您要取消引用非对齐指针,该指针取决于硬件,可能未定义。许多架构假设long*
将与32位内存对齐,即可被4整除。如果不是,则结果未定义。
通常,C不保证将一种指针类型转换为另一种指针类型时会发生什么。