-1

Question of C Standard

Easy question, but couldn't seem to find the answer with a duckduckgo or by searching SO (here).

I am aware that in C, the standard states that uninitialized arrays of ints results in undefined behaviour. (Or at least most compilers behave this way.)

int a[100]; // 100 x 32bits on stack, values are whatever was left over on the stack
printf("a[5]=%d", a[5]); // undefined behaviour, can be any valid `int` value (-2**15 to 2**15 - 1)

However, what is the default value of a single int?

int a; printf("a=%d", a);

My guess would be that since this is on the stack, a "push stack" instruction has to be executed by the CPU, and this instruction MUST take a value, and the most sensible value for a compiler to use if no value is specified would be zero.

Am I correct?

Example Test Program and Disassembly

#include <stdio.h>

int a;
    
int main(void)
{
    printf("%d\n", a);
    return 0;
}

And this is this dissassembly: (`gcc -save-temps -Wall test.c -o test)

    .file   "test.c"
    .text
    .globl  a
    .bss
    .align 4
    .type   a, @object
    .size   a, 4
a:
    .zero   4
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    a(%rip), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
    .section    .note.GNU-stack,"",@progbits

This appears to have the line

a:
    .zero 4

does this mean that a is initialized as a block of memory in the .data section as 4x 0x00 (bytes) ?

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • There is no default value for automatic and dynamic variables. For static variables, the default value is 0, but IIRC it used to be undefined, too. – fuz Mar 02 '21 at 11:38
  • I heard that using value of uninitialized array doesn't invoke undefined behavior because arrays cannot be declared with `register`. – MikeCAT Mar 02 '21 at 11:41
  • 1
    @PeterCordes I heard that here. [arrays - Why does this C code work fine when debugging but not when run normally? - Stack Overflow](https://stackoverflow.com/questions/66369190/why-does-this-c-code-work-fine-when-debugging-but-not-when-run-normally/66369213#comment117335975_66369213) – MikeCAT Mar 02 '21 at 12:13
  • 1
    Re “My guess would be that since this is on the stack, a "push stack" instruction has to be executed by the CPU”: No, space on the stack can be reserved by adjusting the stack pointer, with an add or subtract instruction. Commonly, in some architectures, the code at the start of a routine does one adjustment to the stack pointer to make room for all the data the routine uses on the stack. – Eric Postpischil Mar 02 '21 at 12:17
  • Usually they go into a region called `.bss` and not `.data`. Some systems name `.bss` as a part of `.data` though. – Lundin Mar 02 '21 at 15:07
  • Your `int a;` *is* defined; it looks like a declaration, but it's also taken as a "tentative definition". The C standard makes this equivalent to an initialized definition with `int a = 0;`. – Nate Eldredge Mar 02 '21 at 19:03

1 Answers1

3

In your example

#include <stdio.h>

int a;
    
int main(void)
{
    printf("%d\n", a);
    return 0;
}

The variable a is declared globally, so it is initialized to zero.

On the other hand, if the variable a is declared locally and non-statically

#include <stdio.h>
    
int main(void)
{
    int a;
    printf("%d\n", a);
    return 0;
}

It has an indeterminate value and using the value invokes undefined behavior.

Quote from N1570 6.7.9 Initialization 10:

If an object that has automatic storage duration is not initialized explicitly, its value is
indeterminate. If an object that has static or thread storage duration is not initialized
explicitly, then:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules,
and any padding is initialized to zero bits;
— if it is a union, the first named member is initialized (recursively) according to these
rules, and any padding is initialized to zero bits;

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • Presumably "thread storage duration" means "global variable" in this contect? – FreelanceConsultant Mar 02 '21 at 11:51
  • @FreelanceConsultant: No, global variables have "static storage duration", same as file-scope `static int foo` variables. "Thread storage duration" is thread-local storage, where each thread sees a different copy of the global-scope or file-scope variable. Don't be fooled by the fact that the `static` keyword is only one case of static (allocated at compile-time) storage. – Peter Cordes Mar 02 '21 at 13:17
  • This is all kind of wrong or at least inaccurate. First of all, it is UB because the automatic storage variable didn't have its address taken. Second, reading an indeterminate value invokes _unspecified behavior_ on most systems, unless there are trap representations, which is rarely ever the case of `int`. See my answer at [(Why) is using an uninitialized variable undefined behavior?](https://stackoverflow.com/a/40674888/584518). – Lundin Mar 02 '21 at 15:10
  • @Lundin Any comment on the dissassembly in the question above? – FreelanceConsultant Mar 02 '21 at 18:30