安全地将指向自动变量的指针传递给函数?

假设我有一个函数声明并初始化两个局部变量 – 默认情况下,存储持续时间为auto 。 然后,该函数调用第二个函数,它传递这两个局部变量的地址。 第二个function可以安全地使用这些指针吗?

一个简单的程序化示例,以补充该描述:

 #include  int adder(int *a, int *b) { return *a + *b; } int main() { auto int a = 5; // `auto' is redundant; included for clarity auto int b = 3; // adder() gets the addresses of two auto variables! is this an issue? int result = adder(&a, &b); printf("5 + 3 = %d\n", result); return 0; } 

此程序按预期工作,打印5 + 3 = 8

通常,当我对C有疑问时,我会转向标准,这也不例外。 具体来说,我检查了ISO / IEC 9899 ,§6.2.4。 它在那里说:

4声明标识符没有链接且没有存储类说明符static具有自动存储持续时间

5对于没有可变长度数组类型的对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。 (输入一个封闭的块或调用一个函数会暂停,但不会结束当前块的执行。)如果以递归方式输入该块,则每次都会创建一个新的对象实例。 对象的初始值是不确定的。 如果为对象指定了初始化,则每次在执行块时达到声明时都会执行初始化; 否则,每次达到声明时,该值将变为不确定。

读这篇文章,我提出以下几点:

  1. 变量ab存储持续时间为auto ,我使用auto关键字明确表示。

  2. 在上面的部分引用中,调用adder()函数对应于第5节中的括号。 也就是说,进入adder()函数“暂停但不结束”,执行当前块(即main() )。

  3. 由于main()块不是“以任何方式结束”,因此保证了ab存储。 因此,即使在adder()内部,使用地址&a&b访问它们也应该是安全的。

那么我的问题是:我对此是否正确? 或者我只是“幸运”,并访问记忆位置,偶然,没有被覆盖?


PS我无法通过Google或SO的搜索找到这个问题的确切答案。 如果可以,请将此标记为重复,我将删除它。

是的,它是安全的,基本上你的假设是正确的。 自动对象的生命周期来自声明它的块中的条目,直到块终止。

(C99,6.2.4p5)“对于这样的对象,它的生命周期从进入与其相关联的块延伸,直到该块的执行以任何方式结束。

您的推理对于您的特定函数调用链是正确的,并且您已阅读并引用了标准的相关部分。 这是对局部变量指针的完美有效使用。

如果函数将指针值存储在生命周期长于其自身调用的结构中,则必须谨慎。 考虑两个函数, foo()bar()

 int *g_ptr; void bar (int *p) { g_ptr = p; } void foo () { int x = 10; bar(&x); } int main () { foo (); /* ...do something with g_ptr? */ return 0; } 

在这种情况下,变量x s的生命周期以foo()返回结束。 但是,指向x的指针已经通过bar()存储在g_ptr 。 在这种情况下, foo()将指向其局部变量x的指针传递给bar()

这意味着为了知道将指向局部变量的指针传递给函数是否有效,你必须知道该函数将对它做什么。

这些变量在堆栈中分配。 只要您不从声明它们的函数返回,它们仍然有效。

由于我还没有被允许发表评论,我宁愿写另一个答案作为上述jxh答案的修正案:

有关类似的问题,请在此处查看我的精心解答 。 这包含一个真实世界的例子,即使它遵循所有的c语言规则,被调用函数中的别名使你的代码中断。

即使它在C语言中是合法的,我认为在函数调用中将指针传递给自动变量也是有害的。 你永远不会知道(并且通常你不想知道)被调用的函数对传递的值的作用。 当被调用函数建立别名时,会遇到大麻烦。