"extern" question?

J

jchludzinski

I have 3 files (see below: a.h, w.c, ww.c). I would like to use a
single x (declared somewhere) which would global to both compilation
units: w.c & ww.c. No matter where I place the "extern" qualifier - it
appears to work: x is shared between w.c and ww.c. Or if I simple
don't use "extern", it works. Why? What is the correct (or simply
preferred) usage?

---John

PS> I'm using gcc (GCC) 4.0.2.

/*---------------a.h-----------------*/
#ifndef _A_H
#define _A_H

extern int x;
void g( void );

#endif /* _A_H */

/*---------------w.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

void g( void )
{
x = 12;
fprintf( stderr, "x = %d\n", x );
}

/*---------------ww.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

int main( int argc, char *argv[] )
{
g();
x = 144;
fprintf( stderr, "x = %d\n", x );
g();
fprintf( stderr, "x = %d\n", x );
}
 
J

Jack Klein

I have 3 files (see below: a.h, w.c, ww.c). I would like to use a
single x (declared somewhere) which would global to both compilation
units: w.c & ww.c. No matter where I place the "extern" qualifier - it
appears to work: x is shared between w.c and ww.c. Or if I simple
don't use "extern", it works. Why? What is the correct (or simply
preferred) usage?

The only CORRECT usage is to have one and only definition of the
object (a declaration without the extern keyword, or a declaration
with an initializer with or without the extern keyword) in one
translation unit, and a declaration with the extern keyword and no
initializer in every other translation unit that references the
object.

The preferred usage, is to put the external declaration (with extern,
without initializer) in a header and include that header in all
translation units that reference the object, including the one that
defines it.
PS> I'm using gcc (GCC) 4.0.2.

/*---------------a.h-----------------*/
#ifndef _A_H

Here is something else that is not CORRECT. You violate the language
standard when you define identifiers beginning with two underscores,
or an underscore followed by an upper case letter, in your code. All
such identifiers with this pattern are reserved for the implementation
(compiler). Change this.
#define _A_H

extern int x;
void g( void );

#endif /* _A_H */

/*---------------w.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

void g( void )
{
x = 12;
fprintf( stderr, "x = %d\n", x );
}

/*---------------ww.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

int main( int argc, char *argv[] )
{
g();
x = 144;
fprintf( stderr, "x = %d\n", x );
g();
fprintf( stderr, "x = %d\n", x );
}

The C standard is quite specific about external definitions for
objects or functions. For an object or function that appears in an
external declaration but is not referenced by the program, there may
be either zero or one external definition. For an object or function
in an external declaration that is referenced by the program, there
must be exactly one and only one external definition. If a program
violates these conditions, the result is undefined behavior.

In your snippets, you show three different files that each have an
external definition of the int object x. That invokes undefined
behavior, since you have more than one definition. Once you produce
undefined behavior, the C standard no longer knows or cares what
happens, One possible result of undefined behavior is the program
might do what you expected it to do.
 
A

Ark

Jack Klein wrote:
The only CORRECT usage is to have one and only definition of the
object
<snip>
....with the notable exception of tentative definitions.
- Ark
 
J

Jack Klein

Jack Klein wrote:

<snip>
...with the notable exception of tentative definitions.
- Ark

No, because a tentative definition is not an actual external
definition. 6.9.2 makes this quite clear. An external definition has
an initializer, while a tentative definition does not.

The wording of the standard makes this quite clear:

"If a translation unit contains one or more tentative definitions for
an identifier, and the translation unit contains no external
definition for that identifier, then the behavior is exactly as if the
translation unit contains a file scope declaration of that identifier,
with the composite type as of the end of the translation unit, with an
initializer equal to 0."

So the tentative definition itself is never an actual definition of an
object, but it causes the compiler to act "as if" there were an
external definition, that is one with an initializer of 0.
 
S

sarathy

I have 3 files (see below: a.h, w.c, ww.c). I would like to use a
single x (declared somewhere) which would global to both compilation
units: w.c & ww.c. No matter where I place the "extern" qualifier - it
appears to work: x is shared between w.c and ww.c. Or if I simple
don't use "extern", it works. Why? What is the correct (or simply
preferred) usage?

---John

PS> I'm using gcc (GCC) 4.0.2.

/*---------------a.h-----------------*/
#ifndef _A_H
#define _A_H

extern int x;
void g( void );

#endif /* _A_H */

/*---------------w.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

void g( void )
{
x = 12;
fprintf( stderr, "x = %d\n", x );
}

/*---------------ww.c-----------------*/
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

int x;

int main( int argc, char *argv[] )
{
g();
x = 144;
fprintf( stderr, "x = %d\n", x );
g();
fprintf( stderr, "x = %d\n", x );
}
Hi,

1. First of all extern is not a qualifier , it is a storage class
specifier. Because in C, type qualifier implies const and volatile.

2. Explicit "extern" declaration is needed only if the object
definition is in another source file, which the compiler cannot resolve
at compile time. They will be resolved only while linking the
module/Translation Unit(TU) containing the definition with the file
containing declaration.

3. If the definition is in the same source file as the
declaration,then, the "extern" keyword for declaration is optional.
Since the header is already included, that means the defintion is
present in this TU. Hence no explicit extern is needed.

Regards,
Sarathy
 
A

Ark

Jack said:
No, because a tentative definition is not an actual external
definition. 6.9.2 makes this quite clear. An external definition has
an initializer, while a tentative definition does not.

The wording of the standard makes this quite clear:

"If a translation unit contains one or more tentative definitions for
an identifier, and the translation unit contains no external
definition for that identifier, then the behavior is exactly as if the
translation unit contains a file scope declaration of that identifier,
with the composite type as of the end of the translation unit, with an
initializer equal to 0."

So the tentative definition itself is never an actual definition of an
object, but it causes the compiler to act "as if" there were an
external definition, that is one with an initializer of 0.
What I meant was:

1.
int x; //tentative or not?
The compiler doesn't know until the end of the translation unit.

2.
//four definitions, three of them tentative
int x;
int x;
int x;
int x /*maybe, = 2006, so "as-if" initializer ain't necessarily 0 */;

[I don't think we disagree on anything: I was just picking on "one and
only one". And tentative definitions are very real in that they legalize
(define?) addresses which can be used in looped static initializers like
void *head;
void *tail = &head;
void *head = &tail;
AFAIK, that's what they are for; but maybe there are uses that I don't
know about.
]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top