和朋友一起研究C语言动态分配malloc realloc。但是有一个问题。我考虑内存设置

I was studying with a friend about the C language dynamic allocation malloc realloc. But there was a question. I think about Memory Set

此代码通过在动态分配 5 个数组后达到分配给数组的大小时 (cur) 时将大小增加 3 来写入额外数据。

下面是我写的代码

#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main() {

    int i = 0;
    int* arr = (int*)malloc(sizeof(int) * 5);
    int curs = sizeof(arr) * 5 / sizeof(int);

    while (1) {

        printf("Input Num : (-1 input exit): ");
        scanf("%d", &arr[i]);
        if (arr[i] == -1)
            break;

        if ((i + 1) == curs) {
            arr = (int*)realloc(arr, sizeof(int) * (3 + curs));
            printf("Add Allocation Arr!\n");
            curs += 3;
        }
        i++;
    }
                printf(" Arr : ");

                for (int j = 0; j < i; j++) {
                    printf("%d ", arr[j]);
                }
    free(arr);
    return 0;

}

当我输入如下数据时,它工作正常。

输入数据(输入-1退出):1

输入数据(输入-1退出):2

输入数据(输入-1退出):3

输入数据(输入-1退出):4

输入数据(输入-1退出):5

添加分配到达!

输入数据(输入-1退出):1

输入数据(输入-1退出):2

输入数据(输入-1退出):3

添加分配到达!

输入数据(输入-1 退出):-1

到达:1 2 3 4 5 1 2 3

这里有朋友试过一件事

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {

    int* arr = (int*)malloc(sizeof(int) * 5);
    int i = 0;
    int curs = sizeof(arr) * 5 / sizeof(int);

    while (1) {
        printf("Input Data(Input -1 Exit): ");
        scanf("%d", &arr[i]);
        if (arr[i] != -1) {
            if ((i + 1) == curs) {
                printf("Add Allocation Arr!\n");
                curs += 3;
                i++; // ★<-- don't use realloc, just i++
                continue;
            }
            else {
                i++;
                continue;
            }
        }
        else {
            printf("Arr: ");
            for (int j = 0; j < i; j++) {
                printf("%d ", arr[j]);
            }
            break;
        }
    }

    free(arr);
    return 0;
}

如果你看上面评论中的★,你可以看到数组是通过简单地增加 i 来访问的,而没有重新分配。

当我输入如下数据时,它工作正常。

输入数据(输入-1退出):1

输入数据(输入-1退出):2

输入数据(输入-1退出):3

输入数据(输入-1退出):4

输入数据(输入-1退出):5

添加分配到达!

输入数据(输入-1退出):1

输入数据(输入-1退出):2

输入数据(输入-1退出):3

添加分配到达!

输入数据(输入-1退出):1

输入数据(输入-1退出):2

输入数据(输入-1退出):3

添加分配到达!

输入数据(输入-1 退出):-1

到达:1 2 3 4 5 1 2 3 1 2 3

为什么是这样运行?

出于好奇,我使用下面的代码查看内存状态。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {

    int* arr = (int*)malloc(sizeof(int) * 5);
    int i = 0;
    int curs = sizeof(arr) * 5 / sizeof(int);
    for (i = 0; i < 20; i++)
    {
        printf("%d ", arr[i]);
    }
    free(arr);
    return 0;
}

执行结果如下

-842150451 -842150451 -842150451 -842150451 -842150451 -33686019 7077988 108 -969574923 134254852 12127432 12077032 12127440 12077040 12127448 12077048 2076049408 2076489216 1527808 4456514

我只想使用分配的第 5 个内存访问它。预计访问 arr[4] 索引后会发生错误,但结果打印出来了。很混乱。

C 语言不对数组进行任何类型的边界检查,相信程序员会做正确的事情。这是使其快速的部分原因。但这也意味着,如果您没有做正确的事情,就无法保证您的程序会按预期工作。

你看到的是undefined behavior的表现形式。当您 read/write 超过数组末尾时,程序可能会崩溃,它可能会输出奇怪的结果,或者可能(如本例)看起来正常工作。更糟糕的是,通过对代码进行看似无关的更改,例如未使用的局部变量或调用 printf 进行调试,可能会改变未定义行为的表现方式。

仅仅因为程序可能崩溃并不意味着它

这很好奇:

int curs = sizeof(arr) * 5 / sizeof(int);

如果这是试图计算数组中元素的数量,那么它是不正确的(而且是多余的,因为您已经知道您已经为 5 个元素预留了 space)。 sizeof(arr) 为您提供指向 int 指针 的大小,而不是 int 的大小。老实说,此时它只需要

int curs = 5;

分配和重新分配内存的通用协议如下所示:

/**
 * Define some symbolic constants for the initial size
 * and the number of elements to add.
 */
#define START_SIZE 5
#define EXTENT     3

/**
 * Track the number of elements that have been allocated.
 * Using size_t instead of int since that’s what’s
 * usually used to represent sizes 
 */
size_t size = START_SIZE;

/**
 * Unless you’re compiling this as C++ or using an
 * *ancient* K&R C implementation, you should not
 * cast the result of malloc, calloc, or realloc
 */
int *arr = malloc( sizeof *arr * size ); // sizeof *arr == sizeof (int)

/**
 * ALWAYS check the result of malloc, calloc,
 * and realloc.    
 */
if ( !arr )
{
  /**
   * Handle initial allocation failure here.  In
   * this case we just print an error message and exit.
   */
  fputs( "Initial allocation failure, exiting", stderr );
  exit( 0 );
}

size_t i = 0;

/**
 * In this example, "done” is just a placeholder for whatever
 * logic you use to determine when to end the loop.
 */
while ( !done ) 
{
  if ( i == size ) 
  {
    /**
     * We’ve run out of space in the array, so we
     * need to extend it with realloc.
     *
     * Since realloc can return NULL if it can’t satisfy
     * the request, ALWAYS assign the result to a temporary
     * variable, otherwise you will lose your reference
     * to the previously-allocated memory.
     */
    int *tmp = realloc( arr, sizeof *arr * (size + EXTENT) );
    if ( !tmp )
    {
      /**
       * The realloc operation failed.  How you handle this
       * depends on the needs of you program, but realize
       * that the previously-allocated memory is still
       * there and that arr still points to it.  In this
       * case, we’ll just print an error message and exit
       * the loop.
       */
      fputs( "Realloc failed, not accepting any more input", stderr );
      done = 1;
      continue;
    }
    else
    {
      size += 3;
      arr = tmp;
    }
  }
  /**
   * The following line is just a placeholder for whatever
   * logic you use to obtain the next value for the array.
   * We’ll assume it sets the "done" condition appropriately
   * and doesn’t increment i unnecessarily.
   */
  arr[i++] = new_value();
}