为什么这个循环中的变量指向同一个内存位置?

Luc*_*cky 11 c pointers

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

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

输出:

struct 13205520
struct 13205520
struct 13205520
struct 13205520
struct 13205520
Run Code Online (Sandbox Code Playgroud)

如果不明显,我想要的是在 5 个不同的位置生成 5 个不同struct的 s——嗯,实际上是 5 个不同的指向structs 的指针。我怎样才能做到这一点?

Bot*_*mec 8

的范围bar仅存在于循环的一次迭代中。这意味着在struct foo创建next 时,它将与 old 放在同一位置bar,因为在编译器看来,bar不再需要the 。看看你的例子,你似乎不需要bar一次处理所有的's 。所以你可能会同意他们都在同一个位置。但是,如果您需要一次处理多个,我可以想到两种可能的解决方案。

将范围置于循环之外

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

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
}
Run Code Online (Sandbox Code Playgroud)

然后 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
}
Run Code Online (Sandbox Code Playgroud)

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

  • 回复“这意味着当创建下一个`struct foo`时,它将被放置在与旧的`bar`相同的位置”:这不是它的意思。这是一个常见的结果,但“bar”的生命周期(不是范围)是循环体中块的每次执行这一事实并不暗示这一点。 (3认同)

chq*_*lie 6

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

  • 你不能传递一个结构来printf打印它的地址,转换说明符%u需要一个unsigned int,而不是一个结构。
  • 此外,bar未初始化,传递它具有未定义的行为。
  • 每次迭代都会实例化一个未初始化的新结构,可能在同一位置,一旦您离开for主体进行下一次迭代,它就会超出范围。
  • 打印结构的位置,使用%p和传递(void *)&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]);
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ala 6

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

是的,除了它没有被实例化,只是编译器为它预留了一些内存,或者你的程序表现得好像它确实为它预留了一些内存。

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

这是错误的。C 只保证对象在它们的生命周期内不会相互重叠。你bar自动存储持续时间,一旦程序执行到达} 块的末尾,它的生命周期就会结束。

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

  • 分配一个阵列struct foo具有存储持续时间比的for循环的时间; 现在你可以i用来索引这个数组。

  • 使用带有已malloc分配存储持续时间的动态内存分配可确保对象在内存被解除分配之前一直处于活动状态free


请注意,根本不适合使用

printf("struct %u\n", bar);
Run Code Online (Sandbox Code Playgroud)

%u期望一个,unsigned int但你正在传递一个结构。构造的行为是未定义的,实际上我们无法知道数字的含义是什么13205520或它来自哪里。

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

printf("struct %p\n", (void *)&bar);
Run Code Online (Sandbox Code Playgroud)