Jon Wheelock
Jon Wheelock

Reputation: 395

declaring vs defining a local variable

I read that there is a difference between declaring and defining a global variable. My understanding is that in the code below, "a" is declared outside the main (no memory is allocated when just declared) and defined only when value is really assigned to it. But in case of variable C no memory is allocated because there is no usage of it. In case of variable d, it is declaration and initialization. Or is it that the declaration means declaring a variable using extern keyword?

Also what will happen in case of variable d? Does compiler remove this assuming that it is not used anywhere?

int a;    // declaration
int c;    // declaration
int d=10;
int main (void)
{
    a=50;  // a is now allocated memory
    printf"%d",a);
}

But when it comes to a local variable, do we have the concept only declared but memory is not allocated? I read somewhere that just saying int b; will allocate the memory in stack. Does any memory is allocated to variable b in below case if it is not used? My understanding is "No" because we are not really using that variable and compiler does the optimization and removes b in such case.

void func1(void)
{
int b;
printf("Hello");
}

I would like to know the exact difference between declaring with and without extern, defining with respect to both global scope and local scope.

Update Clarification for not being duplicate question: The exact question I was asking is with respect to local variable declaration, initialization (Memory allocation) and comparison with global variable initialization/declaration. In globals we use the word "tentative" for just declaring tentatively, but not allocating memory. Why the same concept is not seen in locals is what my confusion. What happens if local variable is declared and not used. Will memory is allocated or not in stack.

Upvotes: 2

Views: 1469

Answers (2)

chqrlie
chqrlie

Reputation: 144969

Memory is not allocated upon setting the value of a variable, memory is committed by the linker and the loader to every defined global variable.

Uninitialized global variables are set at program start time to 0, 0.0 or the null pointer depending on their type.

Uninitialized local variables have indeterminate values until they are set. Dereferencing them before they are set invokes undefined behaviour.

In your example, a and b have value 0 when main is invoked, b should not be evaluated in function func1 before setting it.

In pre-Ansi C, one could define the same global variable in multiple source files as long as all definitions were identical and none had an initializer. This coding style is deprecated but still supported for compatibility reasons. Modern linkers will complain about such instances.

Thank you Olaf for your extra precision: int a; is indeed a tentative definition. It is allowed to have another definition further down the same translation unit that will provide an initializer. The rationale for this is to allow initializers to cross refer to other variables, such as in this example:

void *p1;   // tentative definition
void *p2 = &p1;   // regular definition
void *p1 = &p2;  // actual definition with an initializer

static void *p3;   // tentative definition
static void *p4 = &p3;   // regular definition
static void *p3 = &p4;  // actual definition with an initializer

Tentative definitions become actual definitions in the absence of other definitions in the same translation unit.

In the example above, it would be more readable to use the extern keyword and make the tentative definition of p1 just a declaration, but there is no alternative for static variable p3.

Update: Local variables cannot be declared, only defined, with or without an initialier. extern int a; at local scope declares a global variable with extern linkage whose name a is only known within the scope of the declaration. It is not per se a local variable, and this risky and confusing style is highly discouraged.

Upvotes: 2

Petr Skocik
Petr Skocik

Reputation: 60107

When you compile

int a;    // declaration
int c;    // declaration
int d=10;
int main (void)
{
    a=50;  // a is now allocated memory
    printf"%d",a);
}

int main.o and analyze the generated symbols with nm main.o, you'll get:

0000000000000004 C a
0000000000000004 C c
0000000000000000 D d
0000000000000000 T main
                 U printf

From man nm:

"C" The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references.

"D"
the symbol is in the initialized data section.

In other words int a; means tentatively allocate unitialized memory for a and mark that memory with the a symbol. Upon linking, if all translation units have the a symbol of this type, it'll turn into one shared definite ("B") unitialized memory allocation (technically not uninitalized because "unitialized" static data is always zeroed). If one translation unit does int a=42, it'll turn into one shared definite memory allocation initialized to 42. If multiple translation units attempt to set a value to a at the global scope, you'll get a linker error.

If you do extern int e; and you use the symbol e at least once in your code, then the e symbol will be of type U, which is like C, except it's an error if no linked-in translation unit provides it. If you do extern int e and don't use it, no symbol gets generated for it.

To put it shortly, nonextern global declarations do reserve memory and can't be optimized out (except at the linking stage). Unused stuff on the stack can be optimized out.

Upvotes: 0

Related Questions