【问题标题】:C: pointer to malloc'ed heap location plus 4C:指向 malloc 堆位置的指针加 4
【发布时间】:2019-03-11 07:43:19
【问题描述】:

我正在尝试实现一个新的 malloc,它将大小存储在 malloc 区域的前面,然后返回一个指向递增位置的指针(在存储的 unsigned 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,unsigned 的大小是 4,我希望“结果”指针指向 X + 4,对吗?这意味着堆栈中存储“结果”指针的内存位置应包含(原始堆地址位置 + 4)。

【问题讨论】:

  • 当你学习 C 时,我会推荐使用 gcc 编译器并且总是编译为gcc -std=c11 -pedantic-errors -Wall -Wextra。这样你就不会得到有问题的非标准代码。

标签: c malloc


【解决方案1】:

result += sizeof(unsigned); 至少应该给你一个警告(void * 上的指针运算会导致未定义的行为)。

unsigned *result = malloc(size + sizeof size);
result[0] = size;
return result + 1;

应该是更简单的方法。

请注意,对于所有可能的数据类型,返回的内存并没有很好地对齐。如果将此内存用于double 或其他 64 位数据类型,则会遇到麻烦。您应该使用 8 字节数据类型 uint64_t 来存储大小,然后内存块对齐。

【讨论】:

  • 实际上,您可能希望 result 成为 size_tmax_align_t 的并集的指针,以确保对齐(@9​​87654329@ 可能比 uint64_t 更宽,即使在平台上后者已定义)。
【解决方案2】:

除了其他答案中提到的对void * 指针执行指针运算的问题外,您还可能违反了C 标准对从malloc() 等函数返回的内存设置的限制之一。

7.22.3 Memory management functions, paragraph 1 of the C standard 状态:

连续调用aligned_alloccallocmallocrealloc 函数分配的存储顺序和连续性未指定。 如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向具有基本对齐要求的任何类型对象的指针,然后用于访问分配空间中的此类对象或此类对象的数组(直到空间被显式释放)。已分配对象的生命周期从分配一直延伸到解除分配。每个这样的分配都应产生一个指向与任何其他对象不相交的对象的指针。返回的指针指向分配空间的开始(最低字节地址)。如果无法分配空间,则返回空指针。如果请求的空间大小为零,则行为是实现定义的:要么返回空指针,要么行为好像大小是某个非零值,但返回的指针不应用于访问对象.

注意粗体部分。

除非您的系统具有只有四个字节的基本对齐(8 或 16 更为典型),否则您将违反该限制,并且将根据6.3.2.3 Pointers, paragraph 7 为任何基本对齐要求大于的对象类型调用未定义的行为四个字节:

... 如果结果指针未正确对齐引用类型,则行为未定义。 ...

【讨论】:

    【解决方案3】:

    How void pointer arithmetic is happening in GCC

    C 不允许使用 void * 指针类型进行指针运算。

    GNU C 允许它通过考虑void 的大小是1

    resultvoid*,所以 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* 来表示泛型指针。

    【讨论】:

      【解决方案4】:

      例如,如果您首先将类型转换为 char*,则可以使用 void* arithmetic。然后回退到 void*。为了获得更好的对齐,请使用 64 位类型的大小,例如uint64_t.

      #define W_REF_VOID_PTR(ptr,offset) \
          ((void*)((char*) (ptr) + (offset)))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-13
        • 1970-01-01
        • 2012-08-17
        • 1970-01-01
        • 2018-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多