我刚刚完成了工作面试的一部分测试,有一个问题难住了我,甚至用谷歌作为参考。我想看看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    
}

当前回答

size =1024;
alignment = 16;
aligned_size = size +(alignment -(size %  alignment));
mem = malloc(aligned_size);
memset_16aligned(mem, 0, 1024);
free(mem);

希望这是一个最简单的实现,让我知道你的意见。

其他回答

如果有限制,你不能浪费一个字节,那么这个解决方案是有效的: 注意:有一种情况可以无限地执行:D

   void *mem;  
   void *ptr;
try:
   mem =  malloc(1024);  
   if (mem % 16 != 0) {  
       free(mem);  
       goto try;
   }  
   ptr = mem;  
   memset_16aligned(ptr, 0, 1024);

三个稍微不同的答案取决于你如何看待这个问题:

1) Jonathan Leffler的解决方案很好地回答了这个问题,除了要四舍五入到16对齐,你只需要额外的15个字节,而不是16个。

A:

/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;

B:

free(mem);

2)对于一个更通用的内存分配函数,调用者不需要跟踪两个指针(一个使用,一个释放)。因此,在对齐的缓冲区下面存储一个指向“真实”缓冲区的指针。

A:

void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;

B:

if (ptr) free(((void**)ptr)[-1]);

注意,与(1)中只向mem添加了15个字节不同,如果您的实现恰好保证了malloc的32字节对齐(不太可能,但理论上C实现可以有32字节对齐类型),那么这段代码实际上可以减少对齐。如果您所做的只是调用memset_16aligned,那么这并不重要,但如果您为结构体使用内存,那么这可能很重要。

我不确定一个好的修复是什么(除了警告用户返回的缓冲区不一定适合任意结构),因为没有办法通过编程确定特定于实现的对齐保证是什么。我猜在启动时,您可以分配两个或更多的1字节缓冲区,并假设您看到的最糟糕的对齐方式是保证对齐方式。如果你错了,你就浪费了记忆。谁有更好的主意,请说出来…

[Added: The 'standard' trick is to create a union of 'likely to be maximally aligned types' to determine the requisite alignment. The maximally aligned types are likely to be (in C99) 'long long', 'long double', 'void *', or 'void (*)(void)'; if you include <stdint.h>, you could presumably use 'intmax_t' in place of long long (and, on Power 6 (AIX) machines, intmax_t would give you a 128-bit integer type). The alignment requirements for that union can be determined by embedding it into a struct with a single char followed by the union:

struct alignment
{
    char     c;
    union
    {
        intmax_t      imax;
        long double   ldbl;
        void         *vptr;
        void        (*fptr)(void);
    }        u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;

然后,您将使用所请求的对齐(在示例中为16)和上面计算的对齐值中较大的一个。

在(64位)Solaris 10上,来自malloc()的结果的基本对齐方式似乎是32字节的倍数。 ]

在实践中,对齐分配器通常采用一个参数进行对齐,而不是硬连接。因此,用户将传递他们所关心的结构体的大小(或大于或等于2的最小次幂),一切都将正常。

3)使用你的平台提供的:posix_memalign用于POSIX, _aligned_malloc用于Windows。

4)如果你使用C11,那么最干净——可移植和简洁——的选项是使用在这个版本的语言规范中引入的标准库函数aligned_alloc。

不幸的是,在C99中,似乎很难保证在任何符合C99的C实现之间都是可移植的。为什么?因为指针不能保证是平面内存模型中想象的“字节地址”。uintptr_t的表示也不是那么有保证,它本身是一个可选类型。

我们可能知道一些实现使用了表示void *(根据定义,也有char *),这是一个简单的字节地址,但在C99中,它对我们程序员来说是不透明的。一个实现可以用集合{segment, offset}表示一个指针,其中offset在“实际”中可能有谁知道的对齐方式。为什么,指针甚至可以是某种形式的哈希表查找值,甚至是链表查找值。它可以编码边界信息。

在最近的C标准C1X草案中,我们看到了_Alignas关键字。这可能会有所帮助。

C99给我们的唯一保证是内存分配函数将返回一个适合赋值给指向任何对象类型的指针的指针。因为我们不能指定对象的对齐方式,所以我们不能以定义良好的、可移植的方式实现我们自己的分配函数来负责对齐。

如果这种说法是错误的,那就好了。

你也可以添加一些16字节,然后通过添加指针下面的(16-mod)将原始ptr推到16位对齐:

main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );

printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );


free(mem1);
}

也许他们会满足于memalign的知识?正如乔纳森·莱弗勒(Jonathan Leffler)指出的,有两个更新的更可取的函数需要了解。

哦,弗罗林先我一步。但是,如果您阅读了我链接到的手册页,您很可能会理解前面的帖子提供的示例。