简体   繁体   中英

setting pointer to variable declared in local scope in C

I have a question about scoping rules in C that can best be illustrated by the following code:

#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;
}

Running this code prints out

42
42
74

I've compiled it using clang with all warnings and with -fsanitize=undefined .

The variable y is declared in a local scope, and is inaccessible after the closing brace. Nonetheless, we can make a previously-declared pointer refer to that local variable, and we can refer to the contents of that memory even after its scope ends.

While this code might work due to the peculiarities of how the program stack works on my machine, it seems to me that dereferencing x at this point should be undefined behavior. I have a better feel for how I'd answer this question in C++. If we were using some class with a non-trivial destructor, rather than a basic type like int , then its destructor will be called at the closing brace.

Does this code invoke undefined behavior? I don't know the C standard very well, so a citation on the relevant rule would be appreciated.

From N1570 :

6.2.4.2:

If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

6.2.4.5:

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, as do some compound literals.

6.2.4.6:

For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.

Whoops! Dragons.

Yes, this is undefined behavior. From §6.2.4 2 of the C11 Standard:

If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

The variable y has automatic storage duration in the posted code. From §6.2.4 5 :

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration....

The very next paragraph states that:

For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.

So the lifetime of y ends after execution of the enclosing block completes, at which time the value of the pointer x becomes indeterminate, and it is undefined behavior to attempt further access of y through x or any other means.

I find it interesting that gcc-7.2.0 behaves differently after optimization flag is turned on.

  1. gcc -Wall -Wextra -pedantic test.c followed by ./a.out

    42
    42
    74

  2. gcc -O2 -Wall -Wextra -pedantic test.c followed by ./a.out

    test.c: In function 'main':
    test.c:13:5: warning: 'y' is used uninitialized in this function [-Wuninitialized]
    printf("%d\\n", *x);
    ^~~~~~~~~~~~
    42
    0
    74

  3. GCC doc says,

    Turning on optimization flags makes the compiler attempt to improve the performance and/or code size at the expense of compilation time and possibly the ability to debug the program.

    In this specific case, optimization enables us to find bugs more easily.

yes this is undefined behavior. What you did there was store the address of y in x , after the closing braces y isn't accessible anymore but that memory location as well as the data in it still exists, it just happens that the data in it hasn't been changed, in the future it may change depending on the program.

I think the x and y will be allocated the memory in the stack of main function when main function is called. In other words, the memory space of y can be visited by x if x points to the memory of y. After main function call is over, space of x and y will be destroyed.

//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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM