在每次调用时使用新内存地址创建变量的函数
假设有一个函数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()
时,会发生以下四种情况:
-
内存管理单元在堆栈上为其提取一块内存,以保存其参数,局部变量和返回值。
-
局部变量
b
在此堆栈帧中声明,并且由于它未初始化,因此它包含“垃圾数据”(例如,此示例为0xDEADBEEF
)。 -
f()
返回并通过移动“堆栈顶部”指针将其堆栈帧向上移动到main()
堆栈帧。 请注意,f()
的堆栈帧的内容永远不会被清除或清零 -
函数
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
的地址(并因此浪费了)为此目的。
也许您正在寻找的是动态内存分配 ? 您可能希望阅读malloc
, calloc
, realloc
和free
的文档(
prototyped。)
从技术上讲,每个对象都有一个存储类 。 对于像f
这样的自动变量,这是自动的 ,通常在堆栈上分配(但也有无堆栈的机器)。 对于动态分配的对象,存储类是动态的 ,通常从堆中分配(但也有无堆的机器)。
实质上,您不应该依赖具有特定模式或值的地址。