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

rav*_*ron 14 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;
}
Run Code Online (Sandbox Code Playgroud)

该程序按预期工作,打印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()块不是"以任何方式结束",a因此b可以保证存储.因此,使用该地址访问它们&a&b,甚至里面adder(),应该是安全的.

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


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

oua*_*uah 8

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

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


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

在这种情况下,变量xs的生命周期以foo()返回结束.然而,指针x已经存储在g_ptrbar().在这种情况下,它是一个错误foo()的指针传递给它的本地变量xbar().

这意味着,为了知道将指向局部变量的指针传递给函数是否有效,您必须知道该函数将对它执行什么操作.