设置指向C中本地作用域中声明的变量的指针

时间:2017-06-12 01:00:52

标签: c language-lawyer undefined-behavior lifetime

我对C中的作用域规则有疑问,可以通过以下代码进行说明:

#include <stdio.h>

int main(void)
{
    int * x = NULL;

    {
        int y = 42;
        x = &y;
        printf("%d\n", *x);
    }

    printf("%d\n", *x);
    *x = 74;
    printf("%d\n", *x);

    return 0;
}

运行此代码打印出来

42
42
74

我已使用包含所有警告的clang和-fsanitize=undefined编译它。

变量y在本地范围内声明,在结束括号后无法访问。尽管如此,我们可以使一个先前声明的指针引用该局部变量,即使在其范围结束后我们也可以引用该内存的内容。

虽然这段代码可能因程序堆栈在我的机器上工作的特殊性而起作用,但在我看来,此时取消引用x应该是未定义的行为。我对如何用C ++回答这个问题有了更好的感受。如果我们使用一些带有非平凡析构函数的类,而不是像int这样的基本类型,那么它的析构函数将在右大括号中调用。

此代码是否会调用未定义的行为?我不太了解C标准,因此我们会对相关规则的引用表示赞赏。

5 个答案:

答案 0 :(得分:4)

来自N1570

6.2.4.2:

  

如果在其生命周期之外引用对象,则行为未定义。当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。

6.2.4.5:

  

一个对象,其标识符声明没有链接且没有存储类说明符static具有自动存储持续时间,一些复合文字也是如此。

6.2.4.6:

  

对于没有可变长度数组类型的对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。

糟糕!龙。

答案 1 :(得分:4)

是的,这是未定义的行为。来自C11标准的§6.2.4 2

  

如果一个对象在其生命周期之外被引用,那么行为就是   未定义。当指针的值变为不确定时   它指向(或刚刚过去)的对象到达其生命周期的末尾。

变量y在发布的代码中具有自动存储持续时间。来自§6.2.4 5

  

一个对象,其标识符声明没有链接且没有   storage-class specifier static具有自动存储持续时间....

下一段指出:

  

对于没有可变长度数组类型的对象,   它的生命周期从进入到它所在的区块延伸   关联,直到该块的执行以任何方式结束。

因此,y的生命周期在封闭块的执行完成后结束,此时指针x的值变得不确定,并且尝试进一步访问{{1通过y或任何其他方式。

答案 2 :(得分:2)

我觉得有趣的是gcc-7.2.0在启用优化标志后表现不同。

  1. gcc -Wall -Wextra -pedantic test.c后跟./a.out
      

    42
      42个
      74

  2. gcc -O2 -Wall -Wextra -pedantic test.c后跟./a.out

      

    test.c:在函数'main'中:
      test.c:13:5:警告:'y'在此函数中未初始化使用[-Wuninitialized]
       printf(&#34;%d \ n&#34;,* x);
       ^ ~~~~~~~~~~~
      42个
      0
      74

  3. GCC doc说,

      

    启用优化标志会使编译器尝试以编译时间和调试程序的能力为代价来提高性能和/或代码大小。

    在这种特定情况下,优化使我们能够更轻松地发现错误。

答案 3 :(得分:0)

是的,这是未定义的行为。你在那里所做的是将y的地址存储在x中,在结束括号y不再可访问之后,但内存位置及其中的数据仍然存在,它恰好发生在其中的数据没有被改变,将来它可能会根据程序而改变。

答案 4 :(得分:0)

我认为在调用main函数时,x和y将被分配main函数堆栈中的内存。换句话说,如果x指向y的存储器,则y可以访问y的存储空间。主函数调用结束后,x和y的空间将被破坏。

//this is result compiled by gcc
main:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movq    %fs:40, %rax
movq    %rax, -8(%rbp)
xorl    %eax, %eax
movq    $0, -16(%rbp)//this is space of x and initialize the x
movl    $42, -20(%rbp)//this is space of y and initialize the y

//this is result compiled by g++
main:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movq    %fs:40, %rax
movq    %rax, -8(%rbp)
xorl    %eax, %eax
movq    $0, -16(%rbp)//this is space of x and initialize the x
movl    $42, -20(%rbp)//this is space of y and initialize the y