【问题标题】:Variables and allocation inside of a loop循环内的变量和分配
【发布时间】:2012-06-24 15:33:41
【问题描述】:

最近我偶然发现了源代码,其中程序员根据 if/else 条件在一个无限循环中声明变量。我发现奇怪的是代码运行并且它没有暗示任何内存泄漏。首先,我认为这可能是特定于架构的东西(代码是针对 ARM 的),但我进行了一些测试,发现使用 GCC 编译的 IA32 上的二进制文件的行为方式相同。

我的做法是这样的:我创建了两个小程序 foo.c 和 bar.c

Foo.c:

#include <stdio.h>

int main(void)
{
   int i;

   for(i=0; i<10; i++) {
      char buf[10];
      buf[i] = 0;
      printf("buf[%d] (@ %#x) == %d\n", i, &buf, buf[i]);
   }

   return(0);
}

Bar.c:

#include <stdio.h>

int main(void)
{
   int i;

   for(i=0; i<10; i++) {
      char *ptr;
      ptr = (char *) malloc(10);
      ptr[i] = 0;
      printf("buf[%d] (@ %#x) == %d\n", i, &ptr, ptr[i]);
   }

   return(0);
}

Foo.c 中声明数组和在 Bar.c 中分配内存之间做出明确区分的原因是首先我认为编译器可能会自动检测到它是同一个变量,只是忽略了初始 for 迭代后的声明,这在 Bar.c 中当然不应该是这种情况,因为我明确分配了内存。

对我来说真正奇怪的是,在两个示例中,数组和分配内存的地址在初始 for 迭代后保持不变。

我不完全理解这一点,并且我没有我的 K&R 副本,所以我会感谢你的解释。 (另外,如果我的推理有任何错误,我会很高兴指出这一点。)

【问题讨论】:

  • C++ 和 C 是不同的语言,你不知道吗?而return 不是函数...
  • @andy,请用英文写。
  • 如果他们想回答 C 问题,他们会订阅 C 标签。使用不适当的标签来引起对您的问题的更多关注被认为是粗鲁的。
  • @Andy:没有。 C 和 C++ 是不同的语言。而且你不应该转换 malloc() 的返回值,你应该#include &lt;stdlib.h&gt;
  • @andy 在 C 中,你不需要强制转换,这是一种糟糕的风格。在 C++ 中,您必须这样做(但无论如何都应该很少使用 malloc)。不同语言。 非常不同。

标签: c memory-management compiler-construction memory-leaks


【解决方案1】:

不要打印指针ptr 的地址(这是循环内的常量,因为ptr 是当前调用帧中的局部变量),而是打印指针本身:

 printf ("buf[%d] == %d, ptr == %p\n", i, ptr[i], (void*) ptr);

(请记住,如果a是一个数组,你确实有&amp;a == a并且它们的类型是兼容的,但是如果p是一个指针,你通常没有&amp;p == p,它们的类型是不兼容的)

当然,如果您在循环内malloc 某个指针,您通常希望free 该指针位于循环体的末尾。

您应该学习使用gcc -Wall -g 进行编译(在Linux 上,甚至可能还使用-Wextra)并使用调试器gdb(在Linux 上)。

valgrind 是 Linux 上捕获内存泄漏的有用工具。您可以使用Boehm's conservative garbage collector 来“避免”它们(通过使用GC_malloc 而不是malloc,并且不打扰free-ing 显式内存)。

【讨论】:

  • 老实说,应该是gcc -Wall -Wextra,因为-Wall实际上是-Wnot-all(有时使用-Werror很方便)。
  • 我是做 &ptr 还是 ptr 都没关系,因为这与我提出的问题无关。为什么?因为即使我打印出 ptr 而不是 &ptr ,我仍然得到相同的基地址。我在问为什么在每次迭代中使用 malloc 显式分配内存时它是一样的。在这个例子中释放也是没有意义的(因为我想要造成内存泄漏)。
  • @Griwes -Wall 包括大多数通常有用的警告,-Wextra 启用的很多警告是主观的或试图猜测最佳实践。
  • @andy,编码ptr&amp;ptr不一样,所以你说错了:“不管我是&amp;ptr还是ptr都没有关系”跨度>
  • @delnan -Wextra 的示例(来自联机帮助页):缺少 return 语句(或多或少 总是 一个错误),有符号/无符号比较(并不总是错误,但可以隐藏讨厌的错误),缺少字段初始化程序(几乎总是错误)。
【解决方案2】:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int i;
    for(i=0; i<10; i++) {
    char *ptr;
    ptr = malloc(10);
    ptr[i] = 0;
    printf("buf[%d] ptr is %#x, &ptr is %#x, ptr[i] is %d\n", i, ptr, &ptr, ptr[i]);
    }
    return 0;
}

我在 bar.c 的输出中添加了 ptr。现在你可以找到答案了。这是新的源代码 bar.c。我修复了其他人提到的所有错误。你可以通过“gcc -o bar bar.c”编译它并运行“./bar”。

输出是:

    ./bar
    buf[0] ptr is 0x820a008, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[1] ptr is 0x820a018, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[2] ptr is 0x820a028, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[3] ptr is 0x820a038, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[4] ptr is 0x820a048, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[5] ptr is 0x820a058, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[6] ptr is 0x820a068, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[7] ptr is 0x820a078, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[8] ptr is 0x820a088, &ptr is 0xbfb9dc48, ptr[i] is 0
    buf[9] ptr is 0x820a098, &ptr is 0xbfb9dc48, ptr[i] is 0

你可以发现 ptr 的值在每一行都在变化,但 &ptr 的值保持不变。 结论:ptr在每次“for”迭代时分配不同的内存,但ptr的地址并没有改变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 2015-07-07
    • 2012-02-26
    相关资源
    最近更新 更多