【问题标题】:Why does the variable in this loop point at the same memory location? [duplicate]为什么这个循环中的变量指向同一个内存位置? [复制]
【发布时间】:2020-05-03 19:17:57
【问题描述】:

考虑下面的 C 代码。我原以为变量bar 每次都被实例化,因此会指向内存中的不同地址,但事实并非如此。

for (i = 2; i < 7; i++) {
    struct foo bar;
    printf("struct %u\n", bar);
}

输出:

struct 13205520
struct 13205520
struct 13205520
struct 13205520
struct 13205520

如果不是很明显,我想要的是在 5 个不同的位置生成 5 个不同的 structs,实际上是 5 个指向 structs 的不同指针。我该怎么做?

【问题讨论】:

  • 改用malloc()
  • struct foo bar[5];
  • 您显示的代码有未定义的行为unsigned int 的格式说明符如何能够打印结构对象实例?要打印指针,首先需要一个实际的指针,例如使用地址运算符&amp;(如&amp;bar);其次,您需要使用格式说明符%p 来打印void *;第三,你需要将你的指针指向void *
  • @Paul D 如果每次变量都有不同的地址,那么所有内存都会很快耗尽。:)
  • 至于你的“问题”,为什么你认为结构每次迭代都应该获得一个新的位置?既然对象的生命周期随着循环的迭代而结束,为什么不简单地重用现有的内存呢?

标签: c pointers


【解决方案1】:

bar 的范围仅存在于循环的一次迭代中。这意味着当创建下一个struct foo 时,它将与旧的bar 放在同一个位置,因为就编译器而言,bar 不再需要。查看您的示例,您似乎不需要一次处理所有bar。所以你可能会同意他们都在同一个位置。但是,如果您需要一次处理多个,我可以考虑两种可能的解决方案。

将作用域置于循环之外

为此,您需要一个struct foo 的数组。数组的范围需要在循环之外。例如:

struct foo bar_list[5];
for (i = 2; i < 7; i++) {
    printf("struct %p\n", (void *)&bar_list[i - 2]);
    // use the foo's here
}

然后你的 for 循环的每次迭代都可以修改其中一个值

在堆上分配

如果您可以在内存中存储五个指针,您可以将每个条分配到堆上的某个位置。无论如何,您最终可能只会使用数组,因此这可能仅在您需要将结构返回到另一个函数时才有用。你需要做这样的事情:

struct foo* bar_list[5];
for (i = 2; i < 7; i++) {
    bar_list[i - 2] = malloc(sizeof(struct foo));
    printf("struct %p\n", (void *)bar_list[i - 2]);
    // do anything else you need to do
}

还值得一提的是,正如其他人所指出的,%p 将用于打印指针地址。

【讨论】:

  • Re“那意味着当下一个struct foo被创建时,它会和旧的bar放在同一个地方”:不是这个意思。这是一个常见的结果,但bar 的生命周期(不是范围)是循环体中块的每次执行,这并不意味着它。
  • @EricPostpischil 您可能是对的,它可能不一定具有相同的位置,但我相信任何合理的编译器都会将它们放在相同的位置。
  • 请不要告诉别人不真实的事情。不应该期望正在学习新概念的学生能够清除虚假陈述,因为他们还没有所需的知识。虽然这是简单代码中的常见结果,但在某些情况下它可能不会发生,例如,当代码路径包含可变长度数组的定义时。
  • @EricPostpischil 如果我将其更改为类似“它可能/经常与旧的bar 放置在同一位置”会更好吗?
【解决方案2】:

您的代码具有未定义的行为:

  • 您不能将结构传递给printf 以打印其地址,转换说明符%u 需要unsigned int,而不是结构。
  • 此外,bar 未初始化,传递它具有未定义的行为。
  • 每次迭代都会实例化一个未初始化的新结构,可能在同一位置,一旦您离开 for 主体进行下一次迭代,它就会超出范围。
  • 要打印结构的位置,使用%p并传递(void *)&amp;bar,但未指定bar的地址是否每次迭代都相同。

大多数编译器将在每次迭代中为 bar 重复使用相同的空间,但可以想象编译器可以生成代码以专有方式随机化 bar 的地址,以使代码更能抵御攻击。

如果您希望所有结构都驻留在不同的地址中,您可以在循环范围之外定义一个结构数组并以这种方式打印它们的地址:

struct foo bar[5];
for (int i = 2; i < 7; i++) {
    printf("address of struct bar[%d] is %p\n", i - 2, (void *)&bar[i - 2]);
}

【讨论】:

    【解决方案3】:

    我以为变量 bar 每次都被实例化

    是的,除了它没有实例化,只是编译器为其预留了一些内存,或者您的程序的行为就好像它确实预留了一些内存为它。

    因此会指向内存中的不同地址

    这将是错误的。 C 只保证对象在它们的生命周期中不会相互重叠。您的bar 具有自动存储期限,一旦程序执行到达块末尾的},它的生命周期就会结束。

    两种方法可以避免这种情况:

    • 分配一个struct foo数组,其存储持续时间比for 循环长;现在你可以使用i 来索引这个数组了。

    • malloc 使用动态内存分配 - 分配的存储持续时间可确保对象在使用free 释放内存之前一直处于活动状态。


    请注意,使用它根本不合适

    printf("struct %u\n", bar);
    

    %u 需要 unsigned int 但你传入的是一个结构。构造的行为是未定义的,实际上我们无法判断数字13205520 的含义或它的来源。

    要打印bar的地址,您必须使用

    printf("struct %p\n", (void *)&bar);
    

    【讨论】:

      猜你喜欢
      • 2021-10-29
      • 2020-10-29
      • 2016-03-08
      • 1970-01-01
      • 2023-03-16
      • 2011-11-22
      • 2021-06-18
      • 2022-01-25
      • 1970-01-01
      相关资源
      最近更新 更多