C中的内存对齐:返回地址中的偏移量是如何考虑的?
Posted
技术标签:
【中文标题】C中的内存对齐:返回地址中的偏移量是如何考虑的?【英文标题】:Memory alignment in C: How is offset factoring in the return address? 【发布时间】:2021-03-10 18:05:38 【问题描述】:我遇到了以下代码here。
/* Allocate aligned memory in a portable way.
*
* Memory allocated with aligned alloc *MUST* be freed using aligned_free.
*
* @param alignment The number of bytes to which memory must be aligned. This
* value *must* be <= 255.
* @param bytes The number of bytes to allocate.
* @param zero If true, the returned memory will be zeroed. If false, the
* contents of the returned memory are undefined.
* @returns A pointer to `size` bytes of memory, aligned to an `alignment`-byte
* boundary.
*/
void *aligned_alloc(size_t alignment, size_t size, bool zero)
size_t request_size = size + alignment;
char* buf = (char*)(zero ? calloc(1, request_size) : malloc(request_size));
size_t remainder = ((size_t)buf) % alignment;
size_t offset = alignment - remainder;
char* ret = buf + (unsigned char)offset;
// store how many extra bytes we allocated in the byte just before the
// pointer we return
*(unsigned char*)(ret - 1) = offset;
return (void*)ret;
/* Free memory allocated with aligned_alloc */
void aligned_free(void* aligned_ptr)
int offset = *(((char*)aligned_ptr) - 1);
free(((char*)aligned_ptr) - offset);
解释:
char *ret = buf + (unsigned char)offset;
在这里,我们设置了一个新指针,它比 buf 的基地址提前了偏移字节。例如我们想在一个 16 位对齐的内存中分配 68 个字节,它看起来像这样:
requested_size = 68+16 = 84
假设 buf 的基地址是buf = 0x112223341
然后remainder = sizeof(buf)%16 = (84%16) = 4
offset = 16 - 4 = 12 (i.e. 0x0C)
ret = &buf + offset = 0x11223341+0x0C = 0x1122334D
问题:
以下行是做什么的? 我在理解这种语法和实现结果时遇到了一些麻烦。
*(unsigned char*)(ret - 1) = offset
当我们返回 ret
时,那些被分配但绝不是 ret
基地址的一部分的额外字节会发生什么?也就是说,如果我们分配了 16 个额外的字节,但只需要 12 个字节进行对齐,那么剩下的字节会发生什么?
=======更新问题中的代码=======
感谢@ThomasMailund 和他的见解,我认为我可以安全地修改上述代码以简化一些类型转换,如下所示:
/* Allocate aligned memory in a portable way.
*
* Memory allocated with aligned alloc *MUST* be freed using aligned_free.
*
* @param alignment The number of bytes to which memory must be aligned. This
* value *must* be <= 255.
* @param bytes The number of bytes to allocate.
* @param zero If true, the returned memory will be zeroed. If false, the
* contents of the returned memory are undefined.
* @returns A pointer to `size` bytes of memory, aligned to an `alignment`-byte
* boundary.
*/
void *aligned_alloc(size_t alignment, size_t size, bool zero)
size_t request_size = size + alignment;
unsigned char *buf = zero ? calloc(1, request_size) : malloc(request_size);
size_t remainder = ((size_t)buf) % alignment;
size_t offset = alignment - remainder;
unsigned char *ret = buf + (unsigned char)offset;
// store how many extra bytes we allocated in the byte just before the
// pointer we return
*(ret - 1) = offset;
return ret;
/* Free memory allocated with aligned_alloc */
void aligned_free(void* aligned_ptr)
int offset = *(((char*)aligned_ptr) - 1);
free(((char*)aligned_ptr) - offset);
【问题讨论】:
【参考方案1】:对于将来正在查看此内容的任何人:
当我查看void aligned_free
函数时,我想我现在明白了。
我们只是保存在 (ret-1) 的地址处计算的偏移字节数,然后在准备释放内存时将其读回,以释放这些额外的字节。
即从(ret-1)
读取偏移量,从基地址中减去那些我们要求释放的许多#字节以获得原始buf
基地址,然后释放buf
,这样我们就不会遇到内存泄漏!
注意:那些额外的偏移字节,它们仍然分配在内存中,只是没有使用!
【讨论】:
是的。您需要免费提供从 malloc 获得的相同地址,因此您需要返回那里。将输入指针转换为 char * 意味着 -1 向后移动一个字节,您可以在其中获取存储在那里的偏移量。有了偏移量就可以得到原地址。 char 是最小的可寻址单元。通常是一个字节,是的。您可以将 char * 指向任何地方。与其说它指向多少内存,不如说是指针算法在做什么。添加或减去指针,然后将地址 sizeof() 移动到基础类型(有一些限制)。对于 char * sizeof() 是 1。所以如果一个 char 是一个字节,而 p 是 char *,那么 p - 1 是 p 之前的一个字节。如果 ip 是 int * 并且 sizeof(int) 是 4,那么 ip - 1 将是 ip 之前的四个字节。在 void * 上进行指针运算不符合标准,因此必须进行强制转换。 如果您的编译器与 C11 兼容,则不应使用此代码。现在标准中有一个aligned_alloc。 (ret - 1) 是一个字符指针,但它不是您可以分配给它的东西(所谓的左值)。您可以分配给变量,也可以写入地址,但如果您尝试 (res - 1) = offset 您将尝试将地址 (res-1) 更改为其他内容。那是没有意义的。代码的实际作用是 *(res-1) = offset。在这里, *(res-1) 是地址指向的内容,因此您将一个值写入该地址。 Casting真的跟它没什么关系,只不过offset的类型必须是我们可以放入左边的类型中的东西。 如果 res 以 unsigned char * 开头,而不是 char *,则可以避免强制转换。为什么它不是我不了解代码的原因。您可以避免在整个过程中使用无符号字符进行大量转换。以上是关于C中的内存对齐:返回地址中的偏移量是如何考虑的?的主要内容,如果未能解决你的问题,请参考以下文章