【问题标题】:Memory Allocation and Pointers in CC中的内存分配和指针
【发布时间】:2013-04-02 18:31:02
【问题描述】:

我正在为考试而学习,但遇到了一些我觉得很难理解的东西。我们正在处理指针和内存分配,而我只是在玩弄一些东西,试图看看是什么改变了什么。我有这段代码:

int * arr[10];
for(i=0; i<5;i++) 
{
    int index = i;
    arr[index] = malloc(sizeof(int*));
    int i = 2 * index;
    *arr[index] = i;
    printf("arr [%d] = %d\n", index, *arr[index]);  /* should be 0, 2, 4, 6, 8 */
}

但我发现,如果我不使用 *arr[index] = i,而是使用 arr[index] = &i,我就不需要 malloc。我一直认为这两件事本质上是同一件事,但必须有一些我不明白的关键区别才能保证将malloc 与其中一个一起使用。

我真的很困惑为什么我真的需要malloc。我对内存分配相当陌生,我真的不明白什么时候应该使用它(显然)并且想知道是否有人可以为我解决这个问题。

【问题讨论】:

  • 我认为重要的是要理解在堆栈上分配东西(局部变量声明)与从堆中分配内存(malloc)之间的区别。

标签: c pointers malloc


【解决方案1】:

试试这个代码:

int * arr[10];
for(i=0; i<5;i++) 
{
    int index = i;
    int value = 2*i;
    arr[index] = malloc(sizeof(int*));
    *arr[index] = value;
}

for (i=0; i<5; i++)
{
    int index = i;
    printf("arr [%d] = %d\n", index, *arr[index]);  /* should be 0, 2, 4, 6, 8 */
}

如果您进行建议的更改,您现在将有未定义的行为。而这段代码仍然有效。

你会有未定义的行为,因为*arr[0] 现在指向一块已离开作用域的堆栈内存。


您的 malloc 实际上应该是 malloc(sizeof(int))。您正在为int 分配空间,而不是为int *

【讨论】:

  • 不应该分配malloc(sizeof(int));吗?
  • @Armin:当然。这是我没有注意到的代码中的一个错误。
【解决方案2】:

这样写:

*arr[index] = i;

意思是:将i 的值复制到arr[index] 指向的内存位置(在您的代码之前分配的位置)。

arr[index] = &i;

意思是:将i的地址复制到arr[index]

在您的代码中,i 是在 for 循环内自动创建的,并且仅存在于该循环内。离开循环(范围)后,用于存储 i 的内存就可以释放给任何新创建的变量。

正如 sharth 所建议的,尝试查看原始 for 循环之外的值以查看一些有趣的结果。

【讨论】:

    【解决方案3】:

    是的,我认为这很难理解,因为我在 for 中间被重新定义了。我马上重写代码。我写了 i 而不是 index 和 2*i 而不是重新定义的 i。

    int * arr[10];
    for(i=0; i<5;i++) 
    {
        arr[i] = malloc(sizeof(int));
        *arr[i] = 2*i;
        printf("arr [%d] = %d\n", i, *arr[i]);  /* should be 0, 2, 4, 6, 8 */
    }
    

    您实际上并不需要动态内存,您知道将使用数组 0-4。当您不知道需要多少数据时,您需要动态内存。这段代码是这样写的,所以你的其余代码仍然可以工作,但是没有 malloc。

    int array[5];
    int **arr=array;
    

    以下代码的意思是,array[index]应该指向存储i的内存地址。它不会复制i中的值,所以当你改变i,或者i被删除时,这会导致这个指针有问题,以后会引起问题。你不应该这样做。

    arr[index] = &i
    

    【讨论】:

      【解决方案4】:

      一个关键的区别是,一旦i 超出范围,&amp;i 将不复存在(或者,更确切地说,那块内存可以用于其他用途……这可能不会包含您的想法它包含)。

      【讨论】:

        【解决方案5】:

        编辑:我在下面说你没有显示i 是如何声明的。实际上,您重新声明它,如果 i 在循环中使用,则隐藏原始值。无论如何,i 将在循环结束时超出范围,或者很可能在例程结束时。

        你没有在这里显示i 是如何声明的。但是,在大多数情况下,它是一个局部变量,或者可能是传递给方法的参数。在任何一种情况下,该变量的空间都在堆栈上声明。您可以使用&amp;i 获取该变量的地址,但该变量将在方法结束并且代码将这些值从堆栈中弹出后消失。您可能会很幸运,并且只要您需要,该价值就不会受到影响。但是在调用另一个方法的那一刻,该值很可能会被覆盖并繁荣,您的程序充其量只会表现不正确。

        如果i 被全局声明,你就可以侥幸逃脱。

        此外,即使更改了i 的值,您仍指向同一个地址。如果在例程结束时打印出数组的所有值,您会看到它们都是相同的值 - 您放入数组的最后一个值。那是因为数组中的每个条目都指向同一个位置。

        【讨论】:

        • 他们实际上在循环体中使用本地声明的 i 来隐藏它。
        • 啊,你是对的。所以他从循环中隐藏了价值。哎呀!看看循环后数组转储的内容真的很有趣,尽管它可能与 i 的每个前一个化身在同一个地方。
        • 这就是为什么提高编译器的警告级别很有帮助的原因。 =)
        • 此代码在 C99 模式下的 gcc 或 clang 上不会产生错误或警告。
        • 如果您添加-Wshadow,它会发出警告,但这不是-Wall-Wextra 的一部分
        猜你喜欢
        • 2021-11-29
        • 2013-05-06
        • 1970-01-01
        • 1970-01-01
        • 2018-08-11
        • 2016-02-21
        • 2014-02-23
        • 1970-01-01
        相关资源
        最近更新 更多