C避免对齐问题

时间:2018-06-17 18:03:29

标签: c alignment memory-alignment

有些人可以解释一下,下面的例子确实有什么问题,尤其是带有&#34的部分,这可能会导致从不是4的倍数的地址加载32位无符号长整数" :

  

"编译器通常会自然地阻止对齐问题   对齐所有数据类型。事实上,对齐问题通常不是   内核开发人员的主要担忧是gcc人们不得不担心   关于他们。然而,当程序员也玩的时候会出现问题   密切关注指针并访问环境外的数据   由编译器预期。

     

使用较大对齐的重铸指针访问对齐的地址   地址导致对齐问题(无论这可能意味着什么   特殊的架构)。也就是说,这是个坏消息:

char dog[10];
char *p = &dog[1];
unsigned long l = *(unsigned long *)p;
     

此示例将指向char的指针视为指向unsigned的指针   long,这可能导致从32位无符号长度加载   地址不是四的倍数。

     

如果您正在思考,"当我在世界上时,我会这样做吗?"你是   可能是对的。然而,它已经出现了,它将再次出现   小心。现实世界的例子可能不那么明显。"

虽然我不能真正理解这个问题,但是可以通过使用以下代码来解决吗?如果是,为什么?

char * dog = (char *)malloc(10 * sizeof(char));
char *p = dog +1;
unsigned long l = *(unsigned long*)p;

2 个答案:

答案 0 :(得分:1)

您提出的解决方案与引用的解决方案几乎相同,因此它遇到了同样的问题。

错位问题。

当您保留内存时,编译器会使用自动变量(char dog[10])和malloc ed变量保留所需的对齐方式。

当您通过执行指针运算技巧来欺骗编译器时,就像您正在执行的那样,那么它无法保证访问对齐是正确的。

为什么这有问题? 因为,根据您使用的硬件架构,编译器可能会发出需要2或4字节对齐的指令。 例如,ARM有几条指令要求数据以2字节对齐(这就是说,它的地址必须是偶数)。 因此,为ARM处理器构建的代码可能会发出访问冲突。

那么你如何解决问题?

通常使用memcpy

char * dog =malloc(10 * sizeof(char));
char *p = dog;
unsigned long l;
memcpy(&l, p+1, sizeof(l);
//You can use l safely now.

//Copy back l to the array:
memcpy(p+1, &l, sizeof(l);

答案 1 :(得分:0)

你引用的那段话是完全正确的。

大多数情况下,你不必担心对齐,因为编译器会为你处理它,并且这很有效,除非你做一些如此松散的事情,你成功地挫败了编译器试图保护你。

当您致电malloc时,没有问题,因为malloc是特殊的(以多种方式)。除此之外,它还保证返回一个指向适合任何类型对象的存储指针。"

但是,是的,如果你努力工作,你可能会遇到麻烦。回到原来的例子,假设我们有

char dog[] = "My dog Spot";
char *p = &dog[0];
unsigned long l = *(unsigned long *)p;

假设阵列碰巧在内存中布局如下:

      +---+---+---+---+
100:  |   |   | M | y |
      +---+---+---+---+
104:  |   | d | o | g |
      +---+---+---+---+
108:  |   | S | p | o |
      +---+---+---+---+
112:  | t |\0 |   |   |
      +---+---+---+---+

也就是说,假设数组dog最终在内存地址102处,而不是4的倍数。因此指针p也指向地址102,我们尝试访问{{1在地址102处。(您注意到我已将其更改为long int,而不是原始示例中的&dog[0],以试图让事情更加清晰。)

所以我们可能期望变量&dog[1]最终包含1299783780或1679849805(即0x4d792064或0x6420794d),因为它们是前四个字节的表示"我的d"以big-endian或little-endian表示解释。

但由于它是一个不对齐的访问权限,我们可能不会得到任何数字;该程序可能会崩溃,例如"总线错误"代替。

如果我们受到约束并决定做这种事情,我们可以设法自己做这样的调整,用这样的事情:

l

当然,将指针移动char dog[] = "My dog Spot"; char *p = dog; int al = (intptr_t)p % sizeof(unsigned long); al = sizeof(unsigned long) - al; if(al == sizeof(unsigned long)) al = 0; p += al; unsigned long l = *(unsigned long *)p; 直到它指向4的正确倍数,它并没有指向"我的d"更多;现在它指向"狗"

我做过一两次这样的事情,但我无法推荐它。