如何在C中以可移植的方式管理内存对齐和通用指针算法?

如何在C中以可移植的方式管理内存对齐和通用指针算法?,c,pointers,memory-management,C,Pointers,Memory Management,我必须实现malloc/realloc/free的优化版本(为我的特定应用程序定制)。目前代码运行在一个特定的平台上,但如果可能的话,我希望以一种可移植的方式编写它(平台将来可能会改变),或者至少我希望将可能的平台差异集中在一个点上(可能是a.h)。我知道其中一些问题: 记忆排列的差异 适合“通用”分配的最小内存块大小的差异 指针大小的差异 (这里我将忽略内存分配的基本系统服务之间的差异,因为在某些嵌入式系统上它们可能根本不可用。让我们想象一下,我们使用一个大的预分配内存块作为“堆”) 问题

我必须实现malloc/realloc/free的优化版本(为我的特定应用程序定制)。目前代码运行在一个特定的平台上,但如果可能的话,我希望以一种可移植的方式编写它(平台将来可能会改变),或者至少我希望将可能的平台差异集中在一个点上(可能是a.h)。我知道其中一些问题:

  • 记忆排列的差异
  • 适合“通用”分配的最小内存块大小的差异
  • 指针大小的差异
(这里我将忽略内存分配的基本系统服务之间的差异,因为在某些嵌入式系统上它们可能根本不可用。让我们想象一下,我们使用一个大的预分配内存块作为“堆”)

问题:

  • C语言中是否有标准的宏或函数用于这种用途
  • 我在这份工作中还可能面临哪些其他问题

如果你看一下#pragma pack,这可能会对你有所帮助,因为它允许你定义结构打包,并在大多数编译器上实现。

对齐内存因编译器而异不幸的是(这是一个问题),在MSVC上,你有
对齐的#,还有一个在ICC、MSVC和GCC、IIRC下工作的
\u mm\u alloc
,它应该是最可移植的

第二个问题是对齐它造成的内存浪费,这不是什么大问题,但在嵌入式系统上,需要注意

如果要对需要对齐的对象(如SIMD类型)进行堆栈分配,还需要查看
\uuuuu属性(uuuu对齐的uuuuuuuux))
\uuuuu declspec(align(x))


就指针算法的可移植性而言,您可以使用
stdint.h
/
pstdint.h
中的类型来实现这一点,但在
uintptru
和指针之间转换时,标准可能会对UB有所说明(不幸的是,标准不是我的强项:().

对齐功能仅在新的C标准C11中处理。它有关键字
\u Alignof
\u Alignas
和函数
aligned\u alloc
。这些功能不难用大多数现代编译器来模拟(如其他答案所示),因此我建议您自己编写小型宏或包装器,根据
\uuuu STDC\u版本\uuuuu
使用。主要问题是,您只向
malloc()提供内存块的总大小
和朋友,没有关于对象粒度的任何信息。如果将分配视为对象数组,则大小为基本对象的大小,数字n为数组中的对象数,例如:

p = malloc(sizeof(*p) * n);
如果只有总大小,那么就不知道s=4和n=10,或者s=2和n=20,或者s=1和n=40,因为它们都乘以40字节的总大小

因此,基本的问题是,您是否希望直接替换原始函数,例如,当您在整个代码库中抛出本机调用时,或者您的包装器函数是否具有集中式和干式模块化。在这里,您可以使用提供s和n的函数

void *my_malloc (size_t s, size_t n)
大多数情况下,当返回的绝对内存地址是s的倍数时,这应该是一个安全的赌注,以确保正确对齐


或者,在移植实现时,您只需查看本机
malloc()
用于目标平台的对齐方式(例如16的倍数),并将其用于您自己的实现。

确保保持适合所有基本类型的对齐方式的经典方法是定义联合:

union alloc_align {
    void *dummy1;
    long long dummy2;
    long double dummy3;
};
…然后确保您分发的地址始终与从系统内存分配器接收的对齐地址相距
sizeof(union alloc_align)
的倍数


我相信K&R中描述了一种类似的方法。

C说
malloc
返回一个指向内存的指针,该指针用于任何目的。在C中,没有可移植的方法可以通过C特性实现这一点。这导致
malloc
是一个如果用C编写就无法以可移植的方式编写的函数

(C99,7.20.3p1)“如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问所分配空间中的此类对象或此类对象数组(直到空间被显式释放)。”


但是…malloc生成的指针必须保留平台的对齐和打包约束特性。因此,我应该满足而不是修改它们。“#pragma pack”可能会帮助我开发核心分配引擎,这里您是对的。但我的问题更多的是如何知道(和实现)系统约束尽可能标准化。再见!
#pragma pack
绝对是不可移植的。@JanHudec:它适用于MSVC、GCC、ICC、WATCOM和其他一些设备,对我来说似乎非常可移植(但可能没有标准化)@Giuseppe Guerrini:标准与可移植性不同。您接受的答案符合新兴标准,但这些标准尚未在许多平台上实施,因此并不具有特别的可移植性。例如,请查看针对Windows CE或mobile的编译器支持。要获得最大的可移植性,您希望代码已在多年来,de并不是该语言的最新版本。总的来说,你是对的。我的目标是生成一个“编写良好”的核心引擎,其中应用了所有“最佳实践”(_Alignof…),以及一个尽可能小的“系统依赖”层,其中每个SDK的漏洞都得到修复。理想情况下,在未来,所有SDK都应该实现“最佳协议”和“系统依赖”