重新分配以前分配的指向 SIZE_MAX 的指针不会设置 ENOMEM,但重新分配 NULL 有效吗?

Reallocating a previously allocated pointer to SIZE_MAX doesn't set ENOMEM, but reallocating NULL works?

提问人:bryc_wall 提问时间:9/29/2023 更新时间:9/29/2023 访问量:37

问:

问题:

我正在努力重写一些 malloc 函数(、、和),我决定实现一些单元测试,希望能让自己的事情变得更容易一些(而且这似乎是一个很好的做法)。malloccallocreallocfree

在进行设置时,我使用标准的“分配”来确保测试正常运行。不幸的是,我在以下测试中遇到了问题:

#include "unity.h"

#include <stdlib.h>
#include <errno.h>

#include "nmalloc.h"

void realloc_overflow(void)
{
    // Reset ERRNO
    errno = 0;

    // Verify allocating SIZE_MAX sets ENOMEM when reallocating NULL
    TEST_ASSERT_EQUAL_PTR(NULL, my_realloc(NULL, SIZE_MAX)); // <-- Sets ENOMEM
    TEST_ASSERT_ERRNO(ENOMEM);

    // Allocate 112 bytes
    void* ptr = my_malloc(ALLOC_LEN_112U);
    TEST_ASSERT_TRUE(ptr != NULL);

    // Reset ERRNO
    errno = 0;

    // Verify allocating SIZE_MAX sets ENOMEM when reallocating a valid pointer
    // ! This doesn't set errno to ENOMEM on my system? However (SIZE_MAX + 1) does?
    TEST_ASSERT_EQUAL_PTR(NULL, my_realloc(ptr, SIZE_MAX));

    TEST_ASSERT_ERRNO(ENOMEM);

    my_free(ptr);
}

这给出了一个意想不到的输出:

test/Test-nmalloc.c:326:realloc_overflow:FAIL: Expected 12 Was 0

奇怪的是,使用显示了设置的预期行为?realloc(valid_ptr, SIZE_MAX + 1)errno = ENOMEM

注意:如果它变得重要,我正在 M1 Mac 上测试所有这些

尝试解决方案:

我做了一个最小的可行例子:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include "nmalloc.h"

#define ALLOC_LEN_112U 112U

int main(void)
{
    errno = 0;

    // TEST_ASSERT_EQUAL_PTR(NULL, my_realloc(NULL, SIZE_MAX));
    if (NULL == realloc(NULL, SIZE_MAX))
        printf("PASS: null_ptr is NULL.\n");
    else
    {
        printf("ERR: null_ptr is not NULL!\n");
        return 1;
    }

    // TEST_ASSERT_ERRNO(ENOMEM);
    if (errno == ENOMEM)
        printf("PASS: errno is ENOMEM.\n");
    else
    {
        printf("ERR: errno is not NULL!\n");
        return 1;
    }

    void *valid_ptr = malloc(ALLOC_LEN_112U);

    // TEST_ASSERT_TRUE(ptr != NULL);
    if (valid_ptr == NULL)
    {
        printf("ERR: valid_ptr is NULL!\n");
        return 1;
    }
    else
        printf("PASS: valid_ptr is not NULL.\n");

    errno = 0;

    // TEST_ASSERT_EQUAL_PTR(NULL, realloc(ptr, SIZE_MAX));
    if (NULL == realloc(valid_ptr, SIZE_MAX))
        printf("PASS: invalid_ptr is NULL.\n");
    else
    {
        printf("ERR: invalid_ptr is not NULL!\n");
        return 1;
    }

    // TEST_ASSERT_ERRNO(ENOMEM);
    if (errno == ENOMEM)
        printf("PASS: errno is ENOMEM.\n");
    else
    {
        printf("ERR: errno is not NULL!\n");
        return 1;
    }

    free(valid_ptr);

    printf("Success!");
}

这给出了正确的预期输出???

[user@machine ~/test] % clang alloc-test.c nmalloc.o -I./lib -o alloc-test.o && ./alloc-test.o
PASS: null_ptr is NULL.
PASS: errno is ENOMEM.
PASS: valid_ptr is not NULL.
PASS: invalid_ptr is NULL.
PASS: errno is ENOMEM.
Success!
[user@machine ~/test] % 

即使包含库(即添加一个,而不仅仅是将其添加到 clang),一切似乎都正常。所以在这一点上,我不完全确定这里发生了什么,所以一些见解将不胜感激。也许我只是错过了一些微妙(或明显)的东西?jemalloc#include <...>

谢谢!:)

c malloc errno jemalloc unity-test-framework

评论

0赞 pmg 9/29/2023
有些系统采用惰性分配,即......所有分配都“有效”,只是在将来尝试使用内存时才会失败。查看 stackoverflow.com/questions/712683/what-is-lazy-allocation

答:

0赞 0___________ 9/29/2023 #1
  1. SIZE_MAX 不是可以分配的最大内存大小,只能分配 可以保存的最大值。 和 IS 实现定义的行为size_t(SIZE_MAX + 1) == 0realloc

  2. SIZE_MAX远远超过Linux 64中虚拟内存的128TB。所以家庭功能会失败。malloc

奇怪的是,使用 realloc(valid_ptr, SIZE_MAX + 1) 显示 设置 errno = ENOMEM 的预期行为?

(MAX_SIZE + 1)为零,并且标准未指定本例中的行为。

评论

0赞 Barmar 9/29/2023
Realloc 只需要触摸与旧分配大小相对应的新内存部分,因为它必须复制它。