如何对齐malloc分配的堆指针,按BY2PG对齐calloc指针为何触发段错误
堆分配指针对齐方法
- 方法1:使用标准库对齐分配函数(最推荐)
不需要手动调整指针,标准库直接返回符合对齐要求的合法指针:- C11标准的
aligned_alloc:示例用法void *p = aligned_alloc(BY2PG, 0x100000);,要求分配大小是对齐粒度的整数倍,释放直接调用free(p)即可 - POSIX平台的
posix_memalign:示例用法void *p; int ret = posix_memalign(&p, BY2PG, 0x100000);,对齐粒度要求是2的幂且不小于sizeof(void*),释放同样调用free(p)
- C11标准的
- 方法2:手动对malloc返回的指针做对齐
如果需要兼容旧标准无法使用上述函数,可以手动处理,要求分配时多申请冗余空间,且必须保存原始指针用于释放:- 分配时多申请
对齐粒度 - 1 + sizeof(void*)字节的空间,前者用于保证对齐后地址合法,后者用于存储原始指针方便后续释放 - 用
uintptr_t类型处理地址运算,避免32位/64位系统下的地址截断问题
- 分配时多申请
你代码段错误的原因及分配后调整的合法方案
段错误根因
你的代码存在两个致命问题:
- 地址运算类型错误:
roundup函数用u_int和int处理地址,64位系统下指针长度为8字节,转换为4字节的整型会直接截断高位地址,最终得到的vm是非法指针,写入自然触发段错误 - 原始指针丢失:直接覆盖了
calloc返回的原始指针,后续既无法确认对齐后的地址是否在已分配的合法区间内,也无法正常释放内存
注意:即便你修复了类型转换问题,直接修改指针后如果对齐步长超过你预留的冗余,也可能出现对齐后的地址超出分配范围,触发访问异常
分配后调整指针的合法实现
只要满足两个前提就可以合法调整:一是分配时预留足够的对齐冗余空间,二是单独保存原始指针用于释放,修正后的代码如下:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> // 引入uintptr_t用于地址运算 #define BY2PG 0x1000 char *raw_vm; // 保存原始calloc返回的指针 char *vm; // 对齐后的指针 static uintptr_t roundup(uintptr_t n, int align) { if(n % align != 0) { n += (align - (n % align)); } return n; } int main() { // 分配时多申请BY2PG -1字节的冗余,保证对齐后地址落在合法区间 raw_vm = (char *) calloc(0x100000 + BY2PG - 1, sizeof(char)); if (!raw_vm) { perror("calloc failed"); return 1; } // 用uintptr_t做地址运算,避免截断 vm = (char *) roundup((uintptr_t)raw_vm, BY2PG); vm[0] = 5; // 现在访问合法 printf("vm[0] = %d\n", vm[0]); // 释放时必须传原始指针 free(raw_vm); return 0; }
内容的提问来源于stack exchange,提问作者Faiz
相关产品推荐
相关产品推荐

