在每次调用时使用新内存地址创建变量的函数

假设有一个函数f,使得它在其体内包含一个新变量b的声明。 我注意到每次调用f时,变量b – 应该每次创建和销毁(编辑:不在动态情况下,如另一个(现在解决的)相关问题所指出的) – 始终具有相同的内存地址。

以下非常简单的代码显示了一个示例:

#include  #include  void f ( void ){ int b; printf ("%p\n", &b); } int main (){ int i = 0; while (++i <= 10) f(); return 0; } 

输出:(始终使用相同的内存地址)。

是否有可能以这样的方式修改f:在每次调用时,它始终使用新的内存地址创建b,与之前的每个调用不同?

编辑:一些用户建议使用动态分配内存,但不幸的是我仍然无法解决问题。

你可以忽略这个前提,直接看我对这个问题的新表述。

我们考虑以下代码。

 #include  #include  void f ( void ){ int * b = malloc ( sizeof(int) ); printf ("%p\n", &b); } int main (){ int i = 0; while (++i <= 10) f(); return 0; } 

为什么它总是产生相同的输出?

您可能很容易想象,我对C的体验有限。 我们非常欢迎每一个学习更多的建议和/或参考。

谢谢

Pss:在创建一些动态结构数据(如列表或树)之前,我需要先解决这个问题。 我们的想法是,每次添加节点时,新节点必须引用新的内存地址以避免循环。 在我的书中写道,解决方案是使用malloc指令,我试图完全理解为什么。

您正在观察的行为是堆栈帧被创建和回收的结果。 在while循环中重复调用f()时,会发生以下四种情况:

  1. 内存管理单元在堆栈上为其提取一块内存,以保存其参数,局部变量和返回值。

  2. 局部变量b在此堆栈帧中声明,并且由于它未初始化,因此它包含“垃圾数据”(例如,此示例为0xDEADBEEF )。

  3. f()返回并通过移动“堆栈顶部”指针将其堆栈帧向上移动到main()堆栈帧。 请注意, f()的堆栈帧的内容永远不会被清除或清零

  4. 函数f()会立即再次调用,因此会获取一块内存。 这与第一次调用f()时获取的块完全相同(继续并通过打印b的地址进行检查)。 由于b未初始化,它包含“垃圾数据”,但它与f()的第一次调用中的“垃圾数据”( 0xDEADBEEF )相同,因为该位置尚未写入。

堆栈框架图

使用malloc在堆上分配存储可能无法解决此问题! 调用malloc()后跟free()然后再调用malloc()将再次给出堆上的相同地址,如此测试程序所示:

 #include  #include  int main(int argc, char const *argv[]) { char *ptr1 = malloc(sizeof(char)); printf("Allocated memory at %p\n", ptr1); free(ptr1); printf("Freed memory at %p\n", ptr1); char *ptr2 = malloc(sizeof(char)); printf("Allocated memory at %p\n", ptr2); free(ptr2); printf("Freed memory at %p\n", ptr2); if (ptr1 == ptr2) { printf("Pointers were the same!\n"); } return 0; } 

打印出来:

Allocated memory at 0x7f9c49404bf0 Freed memory at 0x7f9c49404bf0 Allocated memory at 0x7f9c49404bf0 Freed memory at 0x7f9c49404bf0 Pointers were the same!

所以我认为你的问题的答案实际上是否定的,你不能修改f() ,以便在每次调用时为b分配一个不同的地址,除非你在f()每次调用中对其进行malloc而不用跟随free()调用 (这是一个内存泄漏)。 这是有道理的,因为如果您需要为每个局部变量使用不同的地址,则需要在每次使用后阻止(泄露)内存。 分配的内存在main()的末尾自动释放,但你可以想象如果你循环超过10次,你可以烧掉你的进程内存。

此代码如下所示:

 #include  #include  void f (void){ int *b = malloc (sizeof(int)); printf ("%p\n", b); } int main (){ int i = 0; while (++i <= 10) f(); return 0; } 

非常接近你所拥有但由于b是一个地址,你可以打印b ,而不是&b

编辑:图片来源为https://andrewharvey4.wordpress.com/tag/avr/

自动变量的地址取决于函数入口上的堆栈指针的值,并且将是一个固定的偏移量。 如果你在这样的循环中调用一个函数:

 while (...) { ... f(...); ... } 

编译器很可能以f的自动变量始终具有相同地址的方式生成代码。 这是你不能轻易改变的。

如果我们不使用自动变量,我们可以轻松获得所需的结果。 只需每次在f分配一个新变量。 当然,这会泄漏大量内存,但是没有更好的方法,因为如果你希望b每次驻留在不同的地址并且以前使用的地址不可用,则必须记住已经用于b的地址(并因此浪费了)为此目的。

也许您正在寻找的是动态内存分配 ? 您可能希望阅读malloccallocreallocfree的文档( prototyped。)

从技术上讲,每个对象都有一个存储类 。 对于像f这样的自动变量,这是自动的 ,通常在堆栈上分配(但也有无堆栈的机器)。 对于动态分配的对象,存储类是动态的 ,通常从堆中分配(但也有无堆的机器)。

实质上,您不应该依赖具有特定模式或值的地址。