realloc() 没有赋值问题

发布于 2024-10-10 14:40:42 字数 1415 浏览 0 评论 0原文

我的一个同学给我发了一个代码,问我怎么了。它是这样的:

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

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            realloc(d_array,(size + 1) * sizeof(int));
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

它应该从用户那里获取数字并保留质数并最后打印它们。我的计算机上的输出类似于:

Enter a number:  3
Is there another number? y/n y
Enter a number:  5
Is there another number? y/n y
Enter a number:  8
Is there another number? y/n y
Enter a number:  7
Is there another number? y/n y
Enter a number:  2
Is there another number? y/n n
4072680
5
7
2

代码中还有其他内容,但最大的问题显然是没有分配 realloc() 的返回值。但奇怪的是,这是我的问题,为什么这段代码显示第一个素数错误,而其他素数正确?动态数组的地址可能会改变,但为什么第二个和其余的都是正确的,而不是第一个?

编辑:好的,我问这个问题的原因是为了尝试理解这段代码中realloc()的行为,如果您有好的资源请分享。当重新分配内存时(当释放旧内存时),realloc() 是否会更改旧内存位置的内容?

One of my classmates sent me a code and asked what was wrong with it. It was something like this:

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

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            realloc(d_array,(size + 1) * sizeof(int));
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

It's supposed to get numbers from the user and keep the ones that are prime and print them in the end. The output on my computer is something like:

Enter a number:  3
Is there another number? y/n y
Enter a number:  5
Is there another number? y/n y
Enter a number:  8
Is there another number? y/n y
Enter a number:  7
Is there another number? y/n y
Enter a number:  2
Is there another number? y/n n
4072680
5
7
2

There were other things in the code but the biggest problem is obviously not assigning the return value of realloc(). But the strange thing is, which is my question, why does this code shows the first prime number wrong and the others correct? The adress of the dynamic array possibly changes but why are the second one and the rest correct and not the first one?

Edit: Ok, the reason why I asked this was to try to understand the behaviour of realloc() in this code, if you have good resources please do share. When reallocating memory (when it is freeing the old one), does realloc() change the content of the old memory location?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鹿童谣 2024-10-17 14:40:43

始终这样做:

void* new_ptr = realloc(ptr, new_size);
if(!new_ptr) error("Out of memory");
ptr = new_ptr;

原因是 realloc() 可能无法在已分配的块中容纳请求的大小。如果需要移动到另一个内存块,它将复制从先前分配的内存中删除数据,释放旧内存块,然后返回新内存块。

另外,如果 realloc() 返回 NULL,则意味着失败。在这种情况下, ptr 指向的内存必须在某个时刻被释放,否则就会出现内存泄漏。换句话说,永远不要这样做:

ptr = realloc(ptr, new_size);

Always do this:

void* new_ptr = realloc(ptr, new_size);
if(!new_ptr) error("Out of memory");
ptr = new_ptr;

The reason is that realloc() might not be able to fit the requested size within the block it already was allocated in. If it needs to move to another memory block, it will copy the data from your previously allocated memory, free the old memory block, and return the new one.

Also, if realloc() returns NULL, it means that it failed. In that case, your memory pointed to by ptr must be freed at some point, otherwise you will have a memory leak. In other words, never do this:

ptr = realloc(ptr, new_size);
懒猫 2024-10-17 14:40:43

如果您调用未定义的行为,那么如果不查看操作系统和编译器内部结构,就无法解释结果。

我知道这不是一个非常令人满意的答案。但是,如果您可以描述其背后的逻辑,那么它就不会真正被称为未定义的行为!

If you are invoking undefined behaviour then there is no way to explain the results without looking at the operating system and compiler internals.

It isn't a very satisfactory answer I know. But then, if you could describe the logic behind it it wouldn't really be called undefined behaviour!

无法回应 2024-10-17 14:40:43

这将向您展示一些正在发生的事情:

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

void dbg_print_array(unsigned sz, const int * array, const char * label) {
     fprintf(stderr, "{%s:\t%p:\t", label, array);
     while (sz--) {
         fprintf(stderr, " %x ", *array++);
     }
     fprintf(stderr, "}\n");
}

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));
    dbg_print_array(size, d_array, "Initial");

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            int * p;
            dbg_print_array(d_array, size, "pre-realloc");
            p = realloc(d_array,(size + 1) * sizeof(int));
            dbg_print_array(d_array, size+1, "post-realloc (d_array)");
            dbg_print_array(p, size+1, "post-realloc (p)");
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

至于为什么这些数据这么快就被覆盖,很难说。不同的堆分配实现可能对此表现得非常不同。由于重新分配是以如此小的步骤完成的(数组每次增长 1)realloc 将被频繁调用。

因为即使您和我通常可能认为未分配的堆空间未使用,但堆分配和释放函数确实会在那里存储一些数据以跟上情况。由于每次调用 realloc 都会读取此数据,并且提供的程序会写入 realloc 可能假定由堆分配例程拥有的数据,因此它可能正在读取该程序已覆盖的内容(它每次都会写掉最初分配的空间的末尾)通过循环)。读取此损坏的数据后,realloc 可能会根据所读取的内容做出决定,从而导致谁知道什么。此时,在程序中,您应该将每个行为视为未定义,因为为正常操作所做的基本假设不再有效。

编辑

通过检查上述代码的输出,您应该能够确定 realloc 实际上何时返回与传递的指针不同的指针(我的猜测是为读入的最后一个整数腾出空间,在您的示例中,因为 malloc 第一次分配可能会舍入为 16 个字节 - 也因为 realloc 没有中止,可能是因为它从未传递过无效指针) 。

当 realloc 未返回与传递的指针相同的指针时,相邻的 post-realloc 打印语句将具有不同的地址(为它们打印的第一个数字)。

This will show you a little bit about what is going on:

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

void dbg_print_array(unsigned sz, const int * array, const char * label) {
     fprintf(stderr, "{%s:\t%p:\t", label, array);
     while (sz--) {
         fprintf(stderr, " %x ", *array++);
     }
     fprintf(stderr, "}\n");
}

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));
    dbg_print_array(size, d_array, "Initial");

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            int * p;
            dbg_print_array(d_array, size, "pre-realloc");
            p = realloc(d_array,(size + 1) * sizeof(int));
            dbg_print_array(d_array, size+1, "post-realloc (d_array)");
            dbg_print_array(p, size+1, "post-realloc (p)");
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

As far as why this data gets over written so soon, it is difficult to tell. Different heap allocation implementations are likely to behave very differently for this. Since the reallocation is done in such small steps (the array grows by 1 each time) realloc will be called often.

Since even though you and I may normally think of the unallocated heap space as unused the heap allocation and freeing functions do store some data there to keep up with things. Since each call to realloc reads this data and the program provided writes to data that realloc probably assumes is owned by the heap allocation routines then it may be reading something that this program has overwritten (it writes off the end of the originally allocated space each time through the loop). After reading this corrupted data realloc may make a decision based on what it read, resulting in who knows what. At this point in the program you should consider every behavior as undefined because the basic assumptions made for normal operation are no longer valid.

edit

By examining the output of the above code you should be able to determine when realloc actually does return a different pointer than the one it was passed (my guess is to make space for the last integer read in, in your example, because malloc probably rounded up to 16 bytes for the first allocation -- also because realloc didn't abort, probably since it was never passed the invalid pointer).

The adjacent post-realloc print statements will have different addresses (first number printed for them) when realloc did not return the same pointer that it was passed.

如若梦似彩虹 2024-10-17 14:40:43

问题在于,在realloc之后,d_array指向的内存被认为是空闲的,因此代码实际上写入了空闲内存。似乎同时内存被分配给不同的东西(由 scanf 使用?),所以它的开头被覆盖。当然,这是完全未定义的:释放的内存的任何部分都可能随时被覆盖。

The problem is that after realloc the memory pointed to by d_array is considered free, so the code actually writes to the free memory. Seems that in the meanwhile the memory is allocated for something different (used by scanf?), so its beginning gets overwritten. Of course, this is completely undefined: any part of the freed memory may be overwritten at any time.

信愁 2024-10-17 14:40:43

由于您似乎最想知道为什么会从未定义的行为中获得可重复(即使不正确)的输出,因此这里有一种情况可能会导致您所看到的结果。我认为理解未定义行为背后的潜在机制可能很有价值 - 请记住,这是假设的,并且可能并不是您所看到的真正原因。对于未定义的行为,您看到的结果可能会从一次编译到下一次编译或从一次运行到下一次运行发生变化(对于一种奇怪的情况,仅更改具有未定义行为的程序中的变量名称就会更改程序的输出,请参阅< a href="https://stackoverflow.com/questions/4575697/unexpected-output-from-bubblesort-program-with-msvc-vs-tcc/4577565#4577565">使用 MSVC 与 TCC 的冒泡排序程序的意外输出)。

  1. d_array 通过在 do/whilemalloc(size * sizeof(int)) 进行初始化code> 进入循环。 d_array 指针在此之后永远不会更改(即使它可能不再指向已分配的内存,正如我们稍后会看到的)

  2. 第一次存储值时,realloc () 被调用,但库发现它不需要更改最初给定的块并返回传递给它的值。因此 3 存储在该块的开头。请注意,这仍然是一个错误,因为您的程序无法知道 d_array 是否仍然有效,因为您忽略了 realloc() 的返回值。< /p>

  3. 当输入5时,会再次调用realloc()。这次,库决定它必须分配不同的块。它会这样做(在复制 d_array 指向的内容之后)。此重新分配的部分结果是 d_array 指向的块被释放,并且库的簿记会用 40726803 >。也许是图书馆关心的事情的指针——谁知道呢。最主要的是,该块现在再次属于库,并且它(而不是您)可以用它做它想做的事。

  4. 现在5被写入d_array + 1(这不是一个有效的操作,因为d_array指向的块已经被释放) 。因此,d_array[0] == 4072680d_array[1] == 5

  5. 从此时起,通过 d_array 存储的所有值都会进入已释放的块,但无论出于何种原因,库都不会注意到正在发生的堆损坏。只是运气好(如果你想找到 bug,那就运气不好了)。没有任何内容被写入 realloc() 可能实际重新分配的块。

注意 - 就像我说的,所有这些都是对该行为的一种可能的解释。实际细节可能有所不同,但这并不重要。一旦您访问了已释放的内存分配(无论是读取还是写入),所有的赌注都会消失。未定义行为的底线规则是任何事情都会发生。

Since you seem to be mostly curious about why you get repeatable (even if incorrect) output from undefined behavior, here's one scenario that could result in what you're seeing. I think understanding the underlying mechanisms behind undefined behavior can be valuable - just keep in mind that this is hypothetical, and might not really be why you're seeing what you're seeing. For undefined behavior the results you see could change from one compile to the next or one run to the next (for an odd case where just changing the name of a variable in a program that had undefined behavior changed the output of the program, see Unexpected output from Bubblesort program with MSVC vs TCC).

  1. d_array is initialized with a call to malloc(size * sizeof(int)) before the do/while loop is entered. The d_array pointer is never changed after this point (even though it might no longer point to allocated memory, as we'll see later)

  2. the first time a value is stored, realloc() is called, but the library finds that it doesn't need to change the block originally given and returns the value that was passed in to it. So 3 is stored at the start of this block. Note that this is still a bug, since your program can't know whether or not d_array is still valid because you've ignored the return value from realloc().

  3. When 5 is entered, another call to realloc() is made. This time, the library decides it does have to allocate a different block. It does so (after copying the contents of what d_array points to). Part of this reallocation results in the the block d_array is pointing to being freed, and the library's bookkeeping overwrites the 3 that was in this block with 4072680. Maybe a pointer to something the library cares about - who knows. The main thing is that the block now belongs to the library again, and it (not you) can do what it wants with it.

  4. Now 5 is written to d_array + 1 (which is not a valid operation, since the block d_array points to has been freed). So d_array[0] == 4072680 and d_array[1] == 5.

  5. from this point on, all values stored through d_array go in to the block that was freed, but for whatever reason the library never notices the heap corruption that's occurring. Just luck (bad luck if you want to find bugs). Nothing ever gets written to blocks that realloc() may have actually reallocated.

Note - like I said, all of this is one possible explanation for the behavior. The actual details might be different, and really don't matter. Once you've accessed a memory allocation that has been freed (whether to read or to write), all bets are off. The bottom line rule for undefined behavior is that anything goes.

长安忆 2024-10-17 14:40:43

尝试一下:

d_array = realloc(d_array,(size + 1) * sizeof(int));

这样做并且在我的计算机上运行良好。

如果你使用 gcc,gcc -Wall -o 会给你你正在寻找的警告。

你 realloc() 但你不使用分配的新内存。简单来说。它仍然指向旧的块(取决于 realloc() 是否使用相同的块或将其移动到不同的位置)。如果你很“幸运”并且使用了同一个块,那么你只需继续写入,否则你将写入旧位置并最终写入不应该写入的位置(因为 realloc() free( ) 在内部更改旧块。 realloc() 不会更改指针变量,只是重新分配空间。正如评论所说,您需要检查分配的新空间是否包含旧缓冲区的数据(只要分配的新内存大于旧内存)。 1)旧内存的内容没有改变,但旧的地方被释放。

try that :

d_array = realloc(d_array,(size + 1) * sizeof(int));

Did that and worked fine on my computer.

If you use gcc a gcc -Wall -o would give you the warning you were looking for.

you realloc() but you don't use the new memory allocated. Simply speaking. It still points to the old one (depending if realloc() used the same block or moved it to a different place). If you were "lucky" and the same block was used you just continue writing, otherwise you are writing to the old place and end up writing in a place you shouldn't (because realloc() free()s the old block internally. realloc() doesn't change your pointer variable, just reallocates space. you need to change the memory address with the result of realloc() returned. And as the comments said. you need to check for successful realloc()ation. the new space allocated contains the data of the old buffer (as long as the new memory allocated is bigger than the old one). the contents of the old memory are not changed but the old place is freed.

攒眉千度 2024-10-17 14:40:43

我认为你必须像这样使用 realloc:

d_array = realloc(d_array,(size + 1) * sizeof(int));

不仅仅是:

realloc(d_array,(size + 1) * sizeof(int));

我看到的另一个问题是 size=1 最初,所以代码第一次运行时是这样做的:

realloc(d_array,(size + 1) * sizeof(int));

size + 1 = 2(为 2 个整数分配内存,但您只需要一个。)解决方案可能是从 0 开始大小。

I think you have to use realloc like this:

d_array = realloc(d_array,(size + 1) * sizeof(int));

and not just:

realloc(d_array,(size + 1) * sizeof(int));

Another problem I see is that size=1 initially, so the first time the code runs it is doing this:

realloc(d_array,(size + 1) * sizeof(int));

size + 1 = 2 (allocating memory for 2 integers, but you only need one.) the solution could be starting size in 0.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文