我正在尝试实现一个新的malloc,它将大小存储在malloc'ed区域的前面,然后返回指向递增位置的指针(存储的无符号int之后的内容)。
void* malloc_new(unsigned size) {
void* result = malloc(size + sizeof(unsigned));
((unsigned*)result)[0] = size;
result += sizeof(unsigned);
return result;
}
我怀疑是否
result += sizeof(unsigned);
行是正确的(做我想做的)。 假设 malloc 堆中的原始地址是 X,无符号的大小是 4,我希望"结果"指针指向 X + 4,对吧?这意味着堆栈中存储"result"指针的内存位置应包含(原始堆地址位置 + 4)。
result += sizeof(unsigned);
应该至少给你一个警告(void *
上的指针算术会导致未定义的行为)。
unsigned *result = malloc(size + sizeof size);
result[0] = size;
return result + 1;
应该是更简单的方法。
请注意,返回的内存并未针对所有可能的数据类型很好地对齐。如果您将此内存用于double
或其他 64 位数据类型,则会遇到麻烦。您应该使用 8 字节数据类型uint64_t
来存储大小,然后内存块会很好地对齐。
除了在指针void *
上执行指针运算的其他答案中指出的问题外,您还可能违反了 C 标准对从malloc()
等函数返回的内存施加的限制之一。
7.22.3 内存管理功能,C 标准第 1 段规定:
通过连续调用
aligned_alloc
、calloc
、malloc
和realloc
函数分配的存储的顺序和连续性是未指定的。如果分配成功,则返回的指针将适当对齐,以便可以将其分配给具有基本对齐要求的任何类型的对象的指针,然后用于访问分配空间中的此类对象或此类对象的数组(直到空间被显式解除分配)。已分配对象的生存期从分配一直延伸到解除分配。每次此类分配都应产生一个指向与任何其他对象不相交的对象的指针。指针返回指向已分配空间的开头(最低字节地址)。如果无法分配空间,则返回空指针。如果请求的空间大小为零,则行为是实现定义的:返回空指针,或者行为好像大小是某个非零值,只是返回的指针不应用于访问对象。
请注意粗体部分。
除非您的系统具有只有四个字节的基本对齐方式(8 或 16 更典型),否则您将违反该限制,并且将根据6.3.2.3 指针第 7 段对基本对齐要求大于 4 个字节的任何对象类型调用未定义的行为:
。如果生成的指针未与引用的类型正确对齐,则行为未定义。...
空指针算术在 GCC 中是如何发生的
C 不允许使用 void * 指针类型进行指针算术。
GNU C 允许它通过考虑
void
的大小是1
。
result
是void*
result += sizeof(unsigned);
所以恰好在兼容的编译器上工作。
您可以将函数重构为:
void *malloc_new(unsigned size) {
void* result = malloc(size + sizeof(unsigned));
((unsigned*)result)[0] = size;
result = (char*)result + sizeof(unsigned);
return result;
}
旁注,在 C 语言中存在void
类型和void*
泛型指针之前,程序员使用char*
来表示泛型指针。
例如,如果您首先将类型转换为 char*,则可以执行 void* 算术。然后投射回虚空*。为了获得更好的对齐效果,请使用 64 位类型作为大小,例如uint64_t
.
#define W_REF_VOID_PTR(ptr,offset)
((void*)((char*) (ptr) + (offset)))