[comp.lang.c] global data in C programming...

btb@ncoast.UUCP (Brad Banko) (03/14/88)

how does one use global data in C programming?
i thought that you just create a header file containing the global data
declarations and then #include it into each of your source files, but
when i try this (using Mark Williams C on an Atari ST), I get
a "symbol redefined" message from the linker.

basically, the type of thing that I am trying to do is:

x.h:
	int i,j;

x.c:
#include "x.h"

xyz() {
	i = ...
}

y.c:
#include "x.h"

zzt() {
	i = j ...
}


K&R doesn't help me out... the stuff about externs and header files is
sort of minimal.

Thanks.


-- 
			Brad Banko
			Columbus, Ohio
			(formerly ...!decvax!cwruecmp!ncoast!btb)
			btb%ncoast@mandrill.cwru.edu

"The only thing we have to fear on this planet is man."
			-- Carl Jung, 1875-1961

karl@haddock.ISC.COM (Karl Heuer) (03/18/88)

In article <7506@ncoast.UUCP> btb@ncoast.UUCP (Brad Banko) writes:
>i thought that you just create a header file containing the global data
>declarations and then #include it into each of your source files, but
>when i try this (using Mark Williams C on an Atari ST), I get
>a "symbol redefined" message from the linker.

Yes.  After the preprocessing phase is through, what you have is a set of
source files each of which contains "int i, j;".  Although this is accepted by
some implementations, it is not portable.

"extern int i, j;" is a declaration; "int i, j;" is a definition.  Each object
must have exactly one definition.  The best$ way to do this is to change your
header file to use the "extern" keyword, and pick one source file (or make a
new one) to contain the definitions.  Thus:

x.h:
	extern int i, j;

x.c:
	#include "x.h"
	xyz() { ... i = ... }

extern.c:
	#include "x.h"
	int i, j;

If you want them to be initialized to non-zero values, put the initializers in
extern.c ("int i=3, j=4;").  (Actually, some implementations seem to require
the initializers anyway, to distinguish a definition from a declaration; so
you might want to add them even if they're zero.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
$ Another popular style, which I do not recommend, is to have x.h contain
"global int i, j;" where "global" is a macro defined with a null expansion in
extern.c but expanding to "extern" for everyone else.

PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) (03/18/88)

>>how does one use global data in C programming?

Basically, the global variables should be declared with the extern
keyword in every source file but one.  (Note that this is really
a linker requirement; I'm not sure whether all environments require
this.)  The way I do this is in the include file, I put
   EXTERN int x,y;
in main (before the file is included) I put
   #define EXTERN
and in all other routines I put
   #define EXTERN extern
If the global variable is explicitly initialized, you need to put in
#ifdef's of some sort so that the initialization is only done where the
extern keyword is absent.

nather@ut-sally.UUCP (Ed Nather) (03/18/88)

In article <3032@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
> In article <7506@ncoast.UUCP> btb@ncoast.UUCP (Brad Banko) writes:
> >i thought that you just create a header file containing the global data
> >declarations and then #include it into each of your source files, but
> >when i try this (using Mark Williams C on an Atari ST), I get
> >a "symbol redefined" message from the linker.
> 
> Yes.  After the preprocessing phase is through, what you have is a set of
> source files each of which contains "int i, j;".  Although this is accepted by
> some implementations, it is not portable.
> 
> $ Another popular style, which I do not recommend, is to have x.h contain
> "global int i, j;" where "global" is a macro defined with a null expansion in
> extern.c but expanding to "extern" for everyone else.

I find that this, too, has limitations, since pre-initialized definitions don't
work.  I finally solved the problem (to my satisfaction anyway) by writing a
program I call "tglob" which takes the "master" set of global definitions,
and turns it into a second file with "extern" inserted in the right places,
and with definitions removed.  It creates a file called "sglob" which can
then be #included in all files except main(), which #includes the definitions.

This way, if I change anything in the gloabl definition file, I needn't also
remember to change it in the other file.  I was ALWAYS making that mistake.
I use "make" and include the dependency that re-creates "sglob" whenever the
global definition file has been changed.

I mailed a few copies of an earlier version that did not handle struct{} 
definitions properly, but didn't keep a list of customers.  If anyone wants
a copy of the program I'll mail it on request.  I use it both under Unix
and with the MSC and Turbo C compilers under MS-DOS.
-- 
Ed Nather
Astronomy Dept, U of Texas @ Austin
{allegra,ihnp4}!{noao,ut-sally}!utastro!nather
nather@astro.AS.UTEXAS.EDU

maart@cs.vu.nl (Maarten Litmaath) (03/18/88)

In article <7506@ncoast.UUCP> btb@ncoast.UUCP (Brad Banko) writes:
\...

\x.h:
\	int i,j;
\
\x.c:
\#include "x.h"
\
\xyz() {
\	i = ...
\}
\
\y.c:
\#include "x.h"
\
\zzt() {
\	i = j ...
\}

\...

You have to reserve space for the variables only in one file, in the
other files you must declare the variables "extern", so the following
is a beautiful solution (thanks to Paul Polderman [polder@cs.vu.nl]):
--------------------------------------------------------------------
global.h:

#ifndef	GLOBAL
#define	GLOBAL	extern
#endif	GLOBAL

GLOBAL	int	i, j;
--------------------------------------------------------------------
global.c:

#define	GLOBAL
#include	"global.h"
--------------------------------------------------------------------
x.c:

#include	"global.h"

xyz()
{
	i = ...
}
--------------------------------------------------------------------
y.c:

#include	"global.h"

zzt()
{
	i = j ...
}
--------------------------------------------------------------------
cc x.c y.c global.c
--------------------------------------------------------------------
Regards.
-- 
South-Africa:                         |Maarten Litmaath @ Free U Amsterdam:
           revival of the Third Reich |maart@cs.vu.nl, mcvax!botter!ark!maart

cox@bentley.UUCP (MH Cox) (03/19/88)

In article <10780@ut-sally.UUCP> nather@ut-sally.UUCP writes:
>In article <3032@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
>> In article <7506@ncoast.UUCP> btb@ncoast.UUCP (Brad Banko) writes:
>> >i thought that you just create a header file containing the global data
>> >declarations and then #include it into each of your source files, but
>> >when i try this (using Mark Williams C on an Atari ST), I get
>> >a "symbol redefined" message from the linker.
>> 
>> Yes.  After the preprocessing phase is through, what you have is a set of
>> source files each of which contains "int i, j;".  Although this is accepted by
>> some implementations, it is not portable.
>> 
>> $ Another popular style, which I do not recommend, is to have x.h contain
>> "global int i, j;" where "global" is a macro defined with a null expansion in
>> extern.c but expanding to "extern" for everyone else.
>
>I find that this, too, has limitations, since pre-initialized definitions don't
>work.  I finally solved the problem (to my satisfaction anyway) by writing a
>program I call "tglob" which takes the "master" set of global definitions,
>and turns it into a second file with "extern" inserted in the right places,
>and with definitions removed.  It creates a file called "sglob" which can
>then be #included in all files except main(), which #includes the definitions.
>
>This way, if I change anything in the gloabl definition file, I needn't also
>remember to change it in the other file.  I was ALWAYS making that mistake.
>I use "make" and include the dependency that re-creates "sglob" whenever the
>global definition file has been changed.

A better way (my opinion) would be to have one file, e.g. globals.c or in
main.c, with all the initialized definitions.  Then create a header
file with all the "extern" declarations without the initializations.
Include this in all your files, INCLUDING the globals.c or main.c.  The{
compiler will then catch any errors of the type:

globals.h:

extern int an_extern_int;


globals.c:

long an_extern_int = 0;		/* wrong type */


A second suggestion is to put all the extern global declarations be
put into one header file.  That makes it easy to "grep" all the files
in the directory to determine which ones access global variables (the
compiler should warn you about files that access global variables but
don't include the header file).

-- 

==========================================================================
Michael H. Cox			ARPA:  cox@garage.nj.att.com
AT&T Bell Labs			UUCP:  ihnp4!bentley!cox
184 Liberty Corner Road		COMPU$ERVE:  76525,3703
Rm 3N-D04
Warren, NJ  07060
(201) 580-8622
==========================================================================

gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/20/88)

In article <1089@bentley.UUCP> cox@bentley.UUCP (59463-MH Cox) writes:
>A better way (my opinion) would be to have one file, e.g. globals.c or in
>main.c, with all the initialized definitions.

Source code that has a large number of global data pooled like this
is woefully out of control.  The proper place for defining a global
datum (of which there should not be many!) is in the module that
controls its meaning.  If there isn't any such module, chances are
that the design is quite poor.

djones@megatest.UUCP (Dave Jones) (03/22/88)

in article <12471@brl-adm.ARPA>, PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) says:
> 
>>>how does one use global data in C programming?
> 
> Basically, the global variables should be declared with the extern
> keyword in every source file but one.  (Note that this is really
> a linker requirement; I'm not sure whether all environments require
> this.)  The way I do this is in the include file, I put
>    EXTERN int x,y;
> in main (before the file is included) I put
>    #define EXTERN
> and in all other routines I put
>    #define EXTERN extern
> If the global variable is explicitly initialized, you need to put in
> #ifdef's of some sort so that the initialization is only done where the
> extern keyword is absent.


Are you aware of the organization SLMA?  I thought not.  Before you 
spend another anguished night, please contact your local chapter of
Silly Little Macros Anonymous.  You owe it to yourself, to your
code, and to generations of code-maintainers to come.  It's only 
the first step, but a step you will never regret.

This is something you should not have to face by yourself.


		One line at a time,


		Dave J.