我刚刚完成了工作面试的一部分测试,有一个问题难住了我,甚至用谷歌作为参考。我想看看StackOverflow的工作人员可以做什么:
memset_16aligned函数需要传递给它一个16字节的对齐指针,否则它将崩溃。
a)如何分配1024字节的内存,并将其对齐到16字节的边界?
b)在memset_16aligned执行后释放内存。
{
void *mem;
void *ptr;
// answer a) here
memset_16aligned(ptr, 0, 1024);
// answer b) here
}
读到这个问题时,我脑子里冒出的第一件事是定义一个对齐的结构,实例化它,然后指向它。
有没有什么根本的原因,因为没有人建议我这么做?
作为旁注,由于我使用了一个char数组(假设系统的char是8位(即1字节)),我认为不一定需要__attribute__((包装))(如果我错了请纠正我),但我还是把它放了进去。
这在我尝试的两个系统上都有效,但有可能是我不知道的编译器优化给了我关于代码有效性的误报。我在OSX上使用gcc 4.9.2,在Ubuntu上使用gcc 5.2.1。
#include <stdio.h>
#include <stdlib.h>
int main ()
{
void *mem;
void *ptr;
// answer a) here
struct __attribute__((packed)) s_CozyMem {
char acSpace[16];
};
mem = malloc(sizeof(struct s_CozyMem));
ptr = mem;
// memset_16aligned(ptr, 0, 1024);
// Check if it's aligned
if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
else printf("Rubbish.\n");
// answer b) here
free(mem);
return 1;
}
不幸的是,在C99中,似乎很难保证在任何符合C99的C实现之间都是可移植的。为什么?因为指针不能保证是平面内存模型中想象的“字节地址”。uintptr_t的表示也不是那么有保证,它本身是一个可选类型。
我们可能知道一些实现使用了表示void *(根据定义,也有char *),这是一个简单的字节地址,但在C99中,它对我们程序员来说是不透明的。一个实现可以用集合{segment, offset}表示一个指针,其中offset在“实际”中可能有谁知道的对齐方式。为什么,指针甚至可以是某种形式的哈希表查找值,甚至是链表查找值。它可以编码边界信息。
在最近的C标准C1X草案中,我们看到了_Alignas关键字。这可能会有所帮助。
C99给我们的唯一保证是内存分配函数将返回一个适合赋值给指向任何对象类型的指针的指针。因为我们不能指定对象的对齐方式,所以我们不能以定义良好的、可移植的方式实现我们自己的分配函数来负责对齐。
如果这种说法是错误的,那就好了。