gpt4 book ai didi

c - 如何仅使用标准库分配对齐的内存?

转载 作者:行者123 更新时间:2023-12-01 14:29:10 25 4
gpt4 key购买 nike

在工作面试中,我刚刚完成了一次测试,一个问题困扰着我,即使是使用Google作为引用。我想看看StackOverflow小组可以做什么:

The memset_16aligned function requires a 16-byte aligned pointer passed to it, or it will crash.

a) How would you allocate 1024 bytes of memory, and align it to a 16 byte boundary?
b) Free the memory after the memset_16aligned has executed.

{    
void *mem;
void *ptr;

// answer a) here

memset_16aligned(ptr, 0, 1024);

// answer b) here
}

最佳答案

原始答案

{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}

固定答案
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}

要求说明

第一步是分配足够的备用空间,以防万一。由于内存必须是16字节对齐的(意味着前导字节地址必须是16的倍数),因此增加16个额外的字节可以保证我们有足够的空间。在前16个字节中的某个位置,有一个16字节对齐的指针。 (请注意, malloc()应该返回一个针对任何目的都充分对齐的指针。但是,“any”的含义主要是针对诸如基本类型( longdoublelong doublelong long以及指向对象的指针和指向的指针)当您在做更专业的事情时,例如在玩图形系统时,它们可能需要比系统其他部分更严格的对齐方式-因此是这样的问题和答案。)

下一步是将void指针转换为char指针;尽管有GCC,您也不应该对空指针进行指针算术(并且GCC具有警告选项,告诉您何时滥用它)。然后将16添加到开始指针。假设 malloc()返回了一个不可能对齐的指针:0x800001。将16相加得出0x800011。现在我想向下舍入到16字节边界-所以我想将最后4位重置为0。0x0F将最后4位设置为1;将0x0F设置为1。因此, ~0x0F将除最后四个以外的所有位都设置为1。将0x800011与Anding得出0x800010。您可以遍历其他偏移量并看到相同的算法。

最后一步 free()很容易:您总是且仅将oj​​it_code, free()malloc()中的一个返回给您的值返回 calloc(),否则将是一场灾难。您正确提供了 realloc()来保持该值-谢谢。免费发布它。

最后,如果您了解系统的 mem包的内部信息,则可以猜测它很可能返回16字节对齐的数据(或者可能是8字节对齐的)。如果它是16字节对齐的,则无需使用这些值。但是,这是狡猾且不可移植的-其他 malloc包具有不同的最小对齐方式,因此,假设做某事时做一件事会导致核心转储。在广泛的范围内,该解决方案是便携式的。

有人提到 malloc是获取对齐内存的另一种方法。并非到处都有,但通常可以以此为基础来实现。请注意,对齐是2的幂是很方便的。其他路线更困惑。

还有一个注释-此代码不检查分配是否成功。

修正案

Windows Programmer指出您不能对指针执行位掩码操作,并且确实,GCC(已测试3.4.6和4.3.1)确实会提示。因此,随后是基本代码的修改版本-转换为主程序。正如我已经指出的,我也自由选择只添加15而不是16。我使用的是 posix_memalign(),因为C99已经存在足够长的时间,可以在大多数平台上访问。如果不是在 uintptr_t语句中使用 PRIXPTR,那么使用 printf()而不是使用 #include <stdint.h>就足够了。 [此代码包括 C.R.指出的修复程序,该修复程序重申了 Bill K数年前首次提出的观点,直到现在我一直忽略了这一点。]
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}

int main(void)
{
void *mem = malloc(1024+15);
void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
return(0);
}

这是一个略为通用的版本,适用于2的幂的大小:
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}

static void test_mask(size_t align)
{
uintptr_t mask = ~(uintptr_t)(align - 1);
void *mem = malloc(1024+align-1);
void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
assert((align & (align - 1)) == 0);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
}

int main(void)
{
test_mask(16);
test_mask(32);
test_mask(64);
test_mask(128);
return(0);
}

为了将 #include <inttypes.h>转换为通用分配函数,分配器的单个返回值将必须对释放地址进行编码,正如几个人在其答案中指出的那样。

面试官的问题

Uri评论:也许今天早上我在阅读理解问题,但是如果面试问题特别指出:“您将如何分配1024字节的内存”,您显然会分配更多的内存。那不是面试官的自动失败吗?

我的回复不适合300个字符的注释...

我想这取决于。我认为大多数人(包括我在内)都认为问题的意思是“您将如何分配一个空间,其中可以存储1024字节的数据,而基地址是16字节的倍数”。如果访调员确实是说您如何才能分配1024个字节(仅)并使其对齐16个字节,那么选择就更加有限了。
  • 显然,一种可能性是分配1024个字节,然后为该地址提供“对齐处理”。这种方法的问题在于,实际的可用空间无法正确确定(可用空间在1008到1024字节之间,但是没有一种机制可以指定哪个大小),这使它变得不那么有用了。
  • 另一种可能性是,您应该编写一个完整的内存分配器,并确保返回的1024字节块正确对齐。如果是这种情况,您可能最终会执行与拟议的解决方案非常相似的操作,但是将其隐藏在分配器中。

  • 但是,如果面试官希望得到这些答复中的任何一个,我希望他们认识到该解决方案可以回答一个密切相关的问题,然后重新构造他们的问题,以使对话指向正确的方向。 (此外,如果面试官真的很刻薄,那我就不想要这份工作;如果对不足够精确的要求的答案在没有纠正的情况下被击落,那么面试官就不是一个可以安全工作的人。)

    世界在前进

    问题的标题最近已更改。困扰我的是解决C访谈中的内存对齐问题。修改后的标题(如何仅使用标准库分配对齐的内存?)要求稍作修改的答案-此附录提供了答案。

    C11(ISO/IEC 9899:2011)添加了功能 test_mask():

    7.22.3.1 The aligned_alloc function

    Synopsis

    #include <stdlib.h>
    void *aligned_alloc(size_t alignment, size_t size);

    Description
    The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate. The value of alignment shall be a valid alignment supported by the implementation and the value of size shall be an integral multiple of alignment.

    Returns
    The aligned_alloc function returns either a null pointer or a pointer to the allocated space.



    POSIX定义 aligned_alloc() :

    #include <stdlib.h>

    int posix_memalign(void **memptr, size_t alignment, size_t size);

    DESCRIPTION

    The posix_memalign() function shall allocate size bytes aligned on a boundary specified by alignment, and shall return a pointer to the allocated memory in memptr. The value of alignment shall be a power of two multiple of sizeof(void *).

    Upon successful completion, the value pointed to by memptr shall be a multiple of alignment.

    If the size of the space requested is 0, the behavior is implementation-defined; the value returned in memptr shall be either a null pointer or a unique pointer.

    The free() function shall deallocate memory that has previously been allocated by posix_memalign().

    RETURN VALUE

    Upon successful completion, posix_memalign() shall return zero; otherwise, an error number shall be returned to indicate the error.



    现在可以使用这两种方法中的一种或两种方法来回答问题,但是最初回答问题时,只有POSIX函数才是选项。

    在幕后,新的对齐内存功能与问题中概述的功能大致相同,不同之处在于它们能够更轻松地强制对齐,并在内部跟踪对齐内存的开始,从而使代码不会必须专门处理-它只是释放使用的分配函数返回的内存。

    关于c - 如何仅使用标准库分配对齐的内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62884224/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com