安全地将指针传递给自动变量来运行?

时间:2013-07-22 22:52:29

标签: c pointers lifetime

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

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

#include <stdio.h>

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

阅读本文,我提出以下几点:

  1. 变量ab的存储时间为auto,我已使用auto关键字对其进行了明确说明。

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

  3. 由于main()块未以任何方式“结束[编辑],”ab的存储空间得到保证。因此,即使在&a内,使用地址&badder()访问它们也应该是安全的。

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


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

4 个答案:

答案 0 :(得分:8)

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

  

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

答案 1 :(得分:7)

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

如果函数将指针值存储在生命周期长于其自身调用的结构中,则必须要小心。考虑两个函数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的生命周期以foo()返回结束。但是,指向x的指针已存储在g_ptr bar()中。在这种情况下,foo()将指向其局部变量x的指针传递给bar()是错误的。

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

有关类似问题,请参阅此处my elaborate answer。这包含一个真实世界的例子,即使它遵循所有的c语言规则,被调用函数中的别名会使你的代码中断。

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