3

我按如下方式填充内存:

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:我知道字节序,我想知道这是正确的,我得到了

11443322

代替

55443322

即,例如,当我有指针时,0x10000001 它会从具有地址 , 和 than 的字节中生成 unsigned 0x10000001long ,0x10000002而不是.0x100000030x100000000x10000005

4

4 回答 4

3

在怀疑内存对齐后,我做了一个快速的谷歌 =)

http://awayitworks.blogspot.co.nz/2010/02/arm-memory-alignment.html

在那篇文章中说:

直到 ARMv4 架构,假定为获取内容提供的地址是内存对齐的……32 位数据获取应该具有与 32 位对齐的地址,依此类推。正如正确猜测的那样,问题仅适用于 32 位和 16 位数据获取。如果数据获取是 32 位,ARM 会忽略地址的低 2 位,如果数据获取是 16 位,则忽略低 1 位。因此,如果地址没有正确对齐,那么数据获取将是错误的。

注意最后一句话=)

如果您需要在 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;
}
于 2012-09-16T22:33:47.230 回答
2

如果你想从内存中取出一个四字节的字,地址应该是四的倍数。

在任何架构上,未对齐的访问通常都是一个坏主意。有些人会抛出一个 SEGFAULT,其他人会透明地处理错误,并且 - 非常缓慢地 - 通过获取包含所需值的两个单词并将它们拼接在一起来合成正确的值。看来(尽管我不是专家)ARM 正在获取指针占用的四字节插槽并旋转结果,以便寄存器的 LSB 与指针匹配。

于 2012-09-16T22:34:39.660 回答
1

字节序不能解释这种行为。似乎 ARM 处理器不允许四字节内存访问未与四字节边界对齐,并且输出表明处理器读取内存,就好像它受到了经过访问的每个字节 8 位的向右按位旋转四字节边界。有关内存对齐的更多信息,请参阅此 Wikipedia 文章

事实上,在某些架构上,如果您尝试执行此类未对齐的内存访问,则可能会发生总线错误。

于 2012-09-16T22:27:20.150 回答
1

问题是您正在取消引用一个未对齐的指针,根据硬件,它可能是未定义的。许多架构假设 along*将与 32 位内存对齐,即可以被 4 整除。如果不是,则结果未定义。

通常,C 不保证将一种指针类型转换为另一种时会发生什么。

于 2012-09-16T22:35:54.083 回答