Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #111644 > unrolled thread

Style question: Declaration of globals in header file

Started bySteve Keller <keller@no.invalid>
First post2017-06-06 19:33 +0200
Last post2017-06-09 09:52 -0700
Articles 20 on this page of 37 — 15 participants

Back to article view | Back to comp.lang.c


Contents

  Style question: Declaration of globals in header file Steve Keller <keller@no.invalid> - 2017-06-06 19:33 +0200
    Re: Style question: Declaration of globals in header file "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-06 14:21 -0400
      Re: Style question: Declaration of globals in header file Steve Keller <keller@no.invalid> - 2017-06-12 11:07 +0200
        Re: Style question: Declaration of globals in header file bartc <bc@freeuk.com> - 2017-06-12 11:03 +0100
        Re: Style question: Declaration of globals in header file Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-12 11:40 +0100
        Re: Style question: Declaration of globals in header file James Kuyper <jameskuyper@verizon.net> - 2017-06-12 07:33 -0400
          Re: Style question: Declaration of globals in header file Steve Keller <keller@no.invalid> - 2017-06-12 18:00 +0200
            Re: Style question: Declaration of globals in header file "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-12 12:11 -0400
              Re: Style question: Declaration of globals in header file Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-12 17:27 +0100
            Re: Style question: Declaration of globals in header file Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-12 17:18 +0100
            Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-12 09:44 -0700
        Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-12 07:55 -0700
          Re: Style question: Declaration of globals in header file Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-12 16:51 +0100
    Re: Style question: Declaration of globals in header file Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-06 19:21 +0100
    Re: Style question: Declaration of globals in header file Jorgen Grahn <grahn+nntp@snipabacken.se> - 2017-06-06 19:54 +0000
    Re: Style question: Declaration of globals in header file Andrey Tarasevich <andreytarasevich@hotmail.com> - 2017-06-06 18:37 -0700
      Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-07 07:54 -0700
        Re: Style question: Declaration of globals in header file David Brown <david.brown@hesbynett.no> - 2017-06-07 17:23 +0200
          Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-07 11:10 -0700
            Re: Style question: Declaration of globals in header file David Brown <david.brown@hesbynett.no> - 2017-06-07 22:13 +0200
              Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-07 14:57 -0700
                Re: Style question: Declaration of globals in header file David Brown <david.brown@hesbynett.no> - 2017-06-08 00:05 +0200
                Re: Style question: Declaration of globals in header file bartc <bc@freeuk.com> - 2017-06-07 23:19 +0100
                  Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-07 16:16 -0700
                Re: Style question: Declaration of globals in header file Ian Collins <ian-news@hotmail.com> - 2017-06-08 12:56 +1200
                  Re: Style question: Declaration of globals in header file David Brown <david.brown@hesbynett.no> - 2017-06-08 08:51 +0200
                    Re: Style question: Declaration of globals in header file David Thompson <dave.thompson2@verizon.net> - 2017-07-09 22:43 -0400
                      Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-07-10 08:46 -0700
                        Re: Style question: Declaration of globals in header file Robert Wessel <robertwessel2@yahoo.com> - 2017-07-10 21:00 -0500
                          Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-07-10 21:08 -0700
                            Re: Style question: Declaration of globals in header file Robert Wessel <robertwessel2@yahoo.com> - 2017-07-11 01:18 -0500
                  Re: Style question: Declaration of globals in header file bartc <bc@freeuk.com> - 2017-06-08 10:10 +0100
                  Re: Style question: Declaration of globals in header file raltbos@xs4all.nl (Richard Bos) - 2017-06-17 18:08 +0000
              Re: Style question: Declaration of globals in header file "Patrick.Schluter" <Patrick.Schluter@free.fr> - 2017-06-10 08:36 +0200
      Re: Style question: Declaration of globals in header file Tim Rentsch <txr@alumni.caltech.edu> - 2017-06-09 10:09 -0700
        Re: Style question: Declaration of globals in header file supercat@casperkitty.com - 2017-06-09 12:17 -0700
    Re: Style question: Declaration of globals in header file Tim Rentsch <txr@alumni.caltech.edu> - 2017-06-09 09:52 -0700

Page 1 of 2  [1] 2  Next page →


#111644 — Style question: Declaration of globals in header file

FromSteve Keller <keller@no.invalid>
Date2017-06-06 19:33 +0200
SubjectStyle question: Declaration of globals in header file
Message-ID<oh6p0c$e9k$1@gioia.aioe.org>
How do you define & declare global variables in implementation and
header files?  Say you have a compilation unit foo.c that offers
global variables foo and bar that are declared in the header file
foo.h for use by other modules.  The C standard allows the
declarations in the header file to be with or without the extern
storage class specifier, i.e. one could write

    -- foo.h --
    int foo;
    int bar;
    -- foo.c --
    #include "foo.h"
    
    int bar = 1;
    ...
    -----------

or

    -- foo.h --
    extern int foo;
    extern int bar;
    -- foo.c --
    #include "foo.h"
    
    int foo;
    int bar = 1;
    ...
    -----------

I slightly prefer the first variant, since zero-initialized variables
don't need to be defined repeatedly in foo.c, i.e. less maintenance
when name or type needs a change.  OTOH, the second variant states
somewhat more explicitly that the variables are defined elsewhere.

So, what do you usually use in such situations?

Steve

[toc] | [next] | [standalone]


#111651

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-06-06 14:21 -0400
Message-ID<6eedb0da-29b9-8a0e-fbec-06a99c76f2bb@verizon.net>
In reply to#111644
On 06/06/2017 01:33 PM, Steve Keller wrote:
> How do you define & declare global variables in implementation and

The C standard doesn't use the term "global variables", and different 
people have their own definitions of what it means. The one that best 
fits this context seems to be "objects with external linkage", so that's 
what I'll assume below.

> header files?  Say you have a compilation unit foo.c that offers
> global variables foo and bar that are declared in the header file
> foo.h for use by other modules.  The C standard allows the
> declarations in the header file to be with or without the extern
> storage class specifier, i.e. one could write
>
>     -- foo.h --
>     int foo;
>     int bar;

Note that this defines a separate set of objects with external linkage 
named "foo" and "bar" in each translation unit that #includes foo.h.

"If an identifier declared with external linkage is used in an 
expression (other than as part of the operand of a sizeof or _Alignof 
operator whose result is an integer constant), somewhere in the entire
program there shall be exactly one external definition for the 
identifier; otherwise, there shall be no more than one." (6.9p5).

A program with 2 or more definitions of foo or bar violates that second 
"shall", so such a program would have undefined behavior. Many 
implementations accept such code, treating all of the different 
definitions for objects named "foo" with external linkage as the same 
object - which can be problematic if those different definitions define 
it with different types or different initial values. In any event, you 
should not write your code to depend upon that.

>     -- foo.c --
>     #include "foo.h"
>
>     int bar = 1;
>     ...
>     -----------
>
> or
>
>     -- foo.h --
>     extern int foo;
>     extern int bar;
>     -- foo.c --
>     #include "foo.h"
>
>     int foo;
>     int bar = 1;
>     ...
>     -----------
>
> I slightly prefer the first variant, since zero-initialized variables
> don't need to be defined repeatedly in foo.c, i.e. less maintenance
> when name or type needs a change.  OTOH, the second variant states
> somewhat more explicitly that the variables are defined elsewhere.

It's sufficiently more explicit, that it has defined behavior, which 
makes it a lot better than the first variant.

[toc] | [prev] | [next] | [standalone]


#111997

FromSteve Keller <keller@no.invalid>
Date2017-06-12 11:07 +0200
Message-ID<ohllk8$17hb$1@gioia.aioe.org>
In reply to#111651
Hm, I wonder why all of you state that having

	int foo;

in multiple translation units violates the standard.  In 6.9.2 this is
called a tentative definiton and my understanding has always been that
you can have multiple tentative definition plus zero or one definitions
with an initializer.  In 6.9.2 the standard at least says that you can
have multiple tentative definition in one translation unit (from some
c99 draft):

[#4] EXAMPLE 1

    int i1 = 1;         // definition, external linkage
    static int i2 = 2;  // definition, internal linkage
    extern int i3 = 3;  // definition, external linkage
    int i4;             // tentative definition, external linkage
    static int i5;      // tentative definition, internal linkage

    int i1;             // valid tentative definition, refers to previous
    int i2;             // 6.2.2 renders undefined, linkage disagreement
    int i3;             // valid tentative definition, refers to previous
    int i4;             // valid tentative definition, refers to previous
    int i5;             // 6.2.2 renders undefined, linkage disagreement

    extern int i1;      // refers to previous, whose linkage is external
    extern int i2;      // refers to previous, whose linkage is internal
    extern int i3;      // refers to previous, whose linkage is external
    extern int i4;      // refers to previous, whose linkage is external
    extern int i5;      // refers to previous, whose linkage is internal

All compilers I know allow multiple tentative also in several
translation units and implement this by putting foo into the common
block (i.e. not .bss or .data section) and the linker allows mutliple
object files to have the same symbol in their common block.  Even the
relatively new LLVM/clang does it that way.

So now I wonder if my understanding of tentative definitions is
totally wrong.  Or do you think about C++ here?  I know that C++'s
rules are more strict.  In C++

	int foo;

normally goes into .bss section so you can't have multiple definitions
of it.

Steve

[toc] | [prev] | [next] | [standalone]


#111999

Frombartc <bc@freeuk.com>
Date2017-06-12 11:03 +0100
Message-ID<QFt%A.173441$6r7.22642@fx34.am4>
In reply to#111997
On 12/06/2017 10:07, Steve Keller wrote:
> Hm, I wonder why all of you state that having
> 
> 	int foo;
> 
> in multiple translation units violates the standard.  In 6.9.2 this is
> called a tentative definiton and my understanding has always been that
> you can have multiple tentative definition plus zero or one definitions
> with an initializer.  In 6.9.2 the standard at least says that you can
> have multiple tentative definition in one translation unit (from some
> c99 draft):
> 
> [#4] EXAMPLE 1
> 
>      int i1 = 1;         // definition, external linkage
>      static int i2 = 2;  // definition, internal linkage
>      extern int i3 = 3;  // definition, external linkage
>      int i4;             // tentative definition, external linkage
>      static int i5;      // tentative definition, internal linkage
> 
>      int i1;             // valid tentative definition, refers to previous
>      int i2;             // 6.2.2 renders undefined, linkage disagreement
>      int i3;             // valid tentative definition, refers to previous
>      int i4;             // valid tentative definition, refers to previous
>      int i5;             // 6.2.2 renders undefined, linkage disagreement
> 
>      extern int i1;      // refers to previous, whose linkage is external
>      extern int i2;      // refers to previous, whose linkage is internal
>      extern int i3;      // refers to previous, whose linkage is external
>      extern int i4;      // refers to previous, whose linkage is external
>      extern int i5;      // refers to previous, whose linkage is internal
> 
> All compilers I know allow multiple tentative also in several
> translation units and implement this by putting foo into the common
> block (i.e. not .bss or .data section) and the linker allows mutliple
> object files to have the same symbol in their common block.  Even the
> relatively new LLVM/clang does it that way.
> 
> So now I wonder if my understanding of tentative definitions is
> totally wrong.  Or do you think about C++ here?  I know that C++'s
> rules are more strict.  In C++
> 
> 	int foo;
> 
> normally goes into .bss section so you can't have multiple definitions
> of it.

It depends on the compiler and linker used.

With two compilers I've tried, linking two modules each containing 'int 
foo;' gives a multiple definition error.

The other compilers where it might work, think they are doing you a 
favour, but are actually performing a disservice, by building in errors 
in programs that will give unexpected problems later. As well as making 
people think writing sloppy code like that is acceptable.

Forget what that bit of standard says as I can't make head or tail or 
it. The requirements are simple: how to declare a variable 'foo' using 
ONE common, shared declaration, that is visible in N modules, but is 
only defined in one.

The pattern that needs to be used is this:

In the shared header:    extern int foo;
In the owner module:     int foo;

All modules will see only 'extern int foo', except the owner module that 
also has 'int foo', so that sees:

  extern int foo;   // in the header
  int foo;          // in the body

So, 'extern' is cancelled by seeing a non-extern definition.

(Because of the bizarre way that C works, the declarations can also be 
in this order:

  int foo;
  extern int foo;

Some compilers even allow you to write:

  extern int foo;       // in the header
  extern int foo=0;     // in the body

so that it is the initialisation that cancels out both 'extern' 
attributes. A sloppy bit of language that leads to sloppy code and poor 
understanding as people will get the impression that anything goes. (I 
won't go into the use of 'extern' inside functions...))

-- 
bartc

[toc] | [prev] | [next] | [standalone]


#112000

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-06-12 11:40 +0100
Message-ID<87ziddcwvn.fsf@bsb.me.uk>
In reply to#111997
Steve Keller <keller@no.invalid> writes:

> Hm, I wonder why all of you state that having
>
> 	int foo;
>
> in multiple translation units violates the standard.  In 6.9.2 this is
> called a tentative definiton

Yup, it's a tentative definition.

> and my understanding has always been that
> you can have multiple tentative definition plus zero or one definitions
> with an initializer.

The fact that it's "tentative" simply means that it is not finally
defined yet, in this translation unit.  It will be properly defined
either by the presence of a non-tentative definition or by the end of
the translation unit.

> In 6.9.2 the standard at least says that you can
> have multiple tentative definition in one translation unit (from some
> c99 draft):

Yes, you can.

> All compilers I know allow multiple tentative also in several
> translation units and implement this by putting foo into the common
> block (i.e. not .bss or .data section) and the linker allows mutliple
> object files to have the same symbol in their common block.  Even the
> relatively new LLVM/clang does it that way.

Sure, but "all compilers I know" is rarely enough!  Have you, for
example, tried tcc?  By default, tcc will reject a program like that.
And gcc can be asked to with -fno-common (which is handy when checking
code for portability).

> So now I wonder if my understanding of tentative definitions is
> totally wrong.

No, not totally wrong, but C does not care how a definition came about.
A translation unit with a tentative definition contains a definition just
as surely as one with an explicit

  int foo = 42;

in it.  The "at most one definition rule" does not care how the
definition came about.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#112004

FromJames Kuyper <jameskuyper@verizon.net>
Date2017-06-12 07:33 -0400
Message-ID<ohltvi$i9r$1@dont-email.me>
In reply to#111997
On 06/12/2017 05:07 AM, Steve Keller wrote:
> Hm, I wonder why all of you state that having
> 
> 	int foo;
> 
> in multiple translation units violates the standard.  In 6.9.2 this is
> called a tentative definiton and my understanding has always been that
> you can have multiple tentative definition plus zero or one definitions
> with an initializer. ...

"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." (6.9.2p2)

"If the declaration of an identifier for an object has file scope and an
initializer, the declaration is an external definition for the
identifier." (6.9.2p1)

Therefore, the "file scope declaration of that identifier ... with an
initializer ..." described in 6.9.2p2 qualifies as an external
definition, despite the fact that the translation unit it's in contains
no actual definition for that identifier, just tentative ones.

"If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof or _Alignof
operator whose result is an integer constant), somewhere in the entire
program there shall be exactly one external definition for the
identifier; otherwise, there shall be no more than one." (6.9p5)

This rule would be violated by tentative definitions becoming external
definitions in two or more translation units.

[toc] | [prev] | [next] | [standalone]


#112043

FromSteve Keller <keller@no.invalid>
Date2017-06-12 18:00 +0200
Message-ID<ohmdr6$mk7$1@gioia.aioe.org>
In reply to#112004
James Kuyper <jameskuyper@verizon.net> writes:

> Therefore, the "file scope declaration of that identifier ... with an
> initializer ..." described in 6.9.2p2 qualifies as an external
> definition, despite the fact that the translation unit it's in contains
> no actual definition for that identifier, just tentative ones.

OK, thanks for this explanation and also the explanation of Ben.  Now,
I get a clearer understanding of these details.

And I will use extern for declarations in header files in the future.

Until now I thought tentative definition are there to allow easier
definition of objects with external linkage (i.e. only one definition
in a header file to be included several times).  Since that is not
allowed, I now wonder what the purpose of tentative definitions is.
I don't see much use of having them in *one* translation unit.

Steve

[toc] | [prev] | [next] | [standalone]


#112046

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-06-12 12:11 -0400
Message-ID<cf613f09-f865-8c49-0628-b6efa617de93@verizon.net>
In reply to#112043
On 06/12/2017 12:00 PM, Steve Keller wrote:
...
> Until now I thought tentative definition are there to allow easier
> definition of objects with external linkage (i.e. only one definition
> in a header file to be included several times).  Since that is not
> allowed, I now wonder what the purpose of tentative definitions is.
> I don't see much use of having them in *one* translation unit.

I gather that at least one of the purposes it's intended to serve is to 
allow mutually recursive external definitions:


     const void *first;
     const void *second = &first;
     const void *first = &second;

That's just the absolutely simplest example I could come up with; it's 
obviously not particularly well-motivated, but I hope it's clear how 
better-motivated cases that depend upon the same rule might come up. I 
had to use void* to keep it this simple; real life cases often involve 
structs containing pointers to other structs, which in turn contain 
pointers back to the original struct.

[toc] | [prev] | [next] | [standalone]


#112052

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-06-12 17:27 +0100
Message-ID<87d1a9b27v.fsf@bsb.me.uk>
In reply to#112046
"James R. Kuyper" <jameskuyper@verizon.net> writes:

> On 06/12/2017 12:00 PM, Steve Keller wrote:
> ...
>> Until now I thought tentative definition are there to allow easier
>> definition of objects with external linkage (i.e. only one definition
>> in a header file to be included several times).  Since that is not
>> allowed, I now wonder what the purpose of tentative definitions is.
>> I don't see much use of having them in *one* translation unit.
>
> I gather that at least one of the purposes it's intended to serve is
> to allow mutually recursive external definitions:
>
>
>     const void *first;
>     const void *second = &first;
>     const void *first = &second;

But that can be done without a tentative definition using extern on the
first declaration.  You could have given an example with all static
objects, but then that leaves the question of the purpose of tentative
definitions of names with external linkage open which is the main thrust
of the thread.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#112048

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-06-12 17:18 +0100
Message-ID<87ink1b2mz.fsf@bsb.me.uk>
In reply to#112043
Steve Keller <keller@no.invalid> writes:

> James Kuyper <jameskuyper@verizon.net> writes:
>
>> Therefore, the "file scope declaration of that identifier ... with an
>> initializer ..." described in 6.9.2p2 qualifies as an external
>> definition, despite the fact that the translation unit it's in contains
>> no actual definition for that identifier, just tentative ones.
>
> OK, thanks for this explanation and also the explanation of Ben.  Now,
> I get a clearer understanding of these details.
>
> And I will use extern for declarations in header files in the future.
>
> Until now I thought tentative definition are there to allow easier
> definition of objects with external linkage (i.e. only one definition
> in a header file to be included several times).  Since that is not
> allowed, I now wonder what the purpose of tentative definitions is.
> I don't see much use of having them in *one* translation unit.

I suspect the it's just the clash of history with pragmatics.  All the
old C compilers merged duplicate definitions so that what later came to
be called tentative were sometimes used as you describe.  But when
standardising the language the committee will have had to decide whether
to /require/ that an implementation be able to do this.  I suspect there
were reports of linkers that could not ensure this behaviour so it was
left undefined.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#112058

Fromsupercat@casperkitty.com
Date2017-06-12 09:44 -0700
Message-ID<d1f315f1-5828-4fd6-bd80-0f29cd5a514a@googlegroups.com>
In reply to#112043
On Monday, June 12, 2017 at 11:00:47 AM UTC-5, Steve Keller wrote:
> Until now I thought tentative definition are there to allow easier
> definition of objects with external linkage (i.e. only one definition
> in a header file to be included several times).  Since that is not
> allowed, I now wonder what the purpose of tentative definitions is.
> I don't see much use of having them in *one* translation unit.

Some implementations had, and continue to have, linkers that won't complain
if a symbol is defined multiple times without using "extern".  Code written
for such implementations would often define symbols multiple times without
bothering to add "extern" qualifiers, since the target implementations
didn't need them.  The authors of the Standard wanted to allow the programs
to remain usable on the platforms whose linkers supported the required
semantics, but for whatever reason seem to have been averse to describing
features that would be supported on some implementations but not others.

[toc] | [prev] | [next] | [standalone]


#112026

Fromsupercat@casperkitty.com
Date2017-06-12 07:55 -0700
Message-ID<a50da75d-91e9-4d74-b504-ff45b4033b08@googlegroups.com>
In reply to#111997
On Monday, June 12, 2017 at 4:07:27 AM UTC-5, Steve Keller wrote:
> Hm, I wonder why all of you state that having
> 
> 	int foo;
> 
> in multiple translation units violates the standard.  In 6.9.2 this is
> called a tentative definiton and my understanding has always been that
> you can have multiple tentative definition plus zero or one definitions
> with an initializer.  In 6.9.2 the standard at least says that you can
> have multiple tentative definition in one translation unit (from some
> c99 draft):

Some object-file formats allow weak symbol definitions which can coexist
with other definitions, but others do not.  The C Standard only requires
that symbols be regarded as "tentative" during the processing of a single
source file.  It somewhat confusingly suggests that implementations are
required to treat tentative definitions exactly like file scope definitions
with an initialization value of zero, but then imposes no requirements
about what should happen if such definitions appear in more than one source
file.

In practice, behaviors which the Standard does not consider "observable"
are not required to match, and implementations will frequently document
ways in which such behaviors *won't* match for declarations of the form
"int varName;" and "int varName=0;".  I see no evidence that the language
of the Standard is intended to forbid such behavioral differences.

[toc] | [prev] | [next] | [standalone]


#112042

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-06-12 16:51 +0100
Message-ID<87r2ypb3x4.fsf@bsb.me.uk>
In reply to#112026
supercat@casperkitty.com writes:

> On Monday, June 12, 2017 at 4:07:27 AM UTC-5, Steve Keller wrote:
>> Hm, I wonder why all of you state that having
>> 
>> 	int foo;
>> 
>> in multiple translation units violates the standard.  In 6.9.2 this is
>> called a tentative definiton and my understanding has always been that
>> you can have multiple tentative definition plus zero or one definitions
>> with an initializer.  In 6.9.2 the standard at least says that you can
>> have multiple tentative definition in one translation unit (from some
>> c99 draft):
>
> Some object-file formats allow weak symbol definitions which can coexist
> with other definitions, but others do not.  The C Standard only requires
> that symbols be regarded as "tentative" during the processing of a single
> source file.  It somewhat confusingly suggests that implementations are
> required to treat tentative definitions exactly like file scope definitions
> with an initialization value of zero, but then imposes no requirements
> about what should happen if such definitions appear in more than one source
> file.

Those two remarks appear to be at odds with each other.  It is precisely
because it states that implementations are required to treat tentative
definitions exactly like file scope definitions with an initialization
value of zero that it imposes the same requirements about what should
happen if such definitions appear in more than one source file as it
does for any other defintion.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#111652

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-06-06 19:21 +0100
Message-ID<87k24pt1s1.fsf@bsb.me.uk>
In reply to#111644
Steve Keller <keller@no.invalid> writes:

> How do you define & declare global variables in implementation and
> header files?  Say you have a compilation unit foo.c that offers
> global variables foo and bar that are declared in the header file
> foo.h for use by other modules.  The C standard allows the
> declarations in the header file to be with or without the extern
> storage class specifier, i.e. one could write

If foo.h is included only in foo.c you don't need a "global" and if
foo.h is included in more than one translation unit, then foo and bar
will be defined more than once and that's not portable.  A C
implementation is not obliged to report it, but it is also permitted to
reject such a program.  Some accept it, some don't.

>     -- foo.h --
>     int foo;
>     int bar;
>     -- foo.c --
>     #include "foo.h"
>     
>     int bar = 1;
>     ...
>     -----------
>
> or
>
>     -- foo.h --
>     extern int foo;
>     extern int bar;
>     -- foo.c --
>     #include "foo.h"
>     
>     int foo;
>     int bar = 1;
>     ...
>     -----------
>
> I slightly prefer the first variant, since zero-initialized variables
> don't need to be defined repeatedly in foo.c, i.e. less maintenance
> when name or type needs a change.  OTOH, the second variant states
> somewhat more explicitly that the variables are defined elsewhere.
>
> So, what do you usually use in such situations?

Only the second is really correct (unless you have only one translation
unit and then why put foo and bar in a header).

The details involve a certain amount to technical fussiness since the
modern rules were written so as to accommodate existing practice, but
the key is to distinguish between something that is just a declaration
and a definition.  All objects in C must have at most one definition
across all translation units.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#111657

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2017-06-06 19:54 +0000
Message-ID<slrnoje22t.1kjn.grahn+nntp@frailea.sa.invalid>
In reply to#111644
On Tue, 2017-06-06, Steve Keller wrote:
> How do you define & declare global variables in implementation and
> header files?  [...]

If I have to have a global, I use "extern", and don't even consider
any other option, if there is one.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#111669

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2017-06-06 18:37 -0700
Message-ID<oh7l5m$ble$1@dont-email.me>
In reply to#111644
On 6/6/2017 10:33 AM, Steve Keller wrote:
> The C standard allows the
> declarations in the header file to be with or without the extern
> storage class specifier, i.e. one could write
>
>     -- foo.h --
>     int foo;
>     int bar;
>     -- foo.c --
>     #include "foo.h"
>
>     int bar = 1;
>     ...
>     -----------

Nope. C standard does not allow that, assuming that you include this 
header file into multiple translation units.

This approach to global declarations is specific to "Relaxed Ref/Def" 
model (per "C99 Rationale V5.10" nomenclature), which was popular in 
native UNIX compilers when C was first standardized. However, this 
approach was rejected by the committee/WG in favor of mixed "Strict 
Ref/Def"+"Initialization" model (same nomenclature). Standard model does 
not allow multiple definitions of the same external object, even if 
these definitions do not include an initializer.

You can read more about the history of that matter "6.2.2 Linkages of 
identifiers" of publicly available "C99 Rationale V5.10" document.

If your implementation accepts the above (again, assuming that you 
include this header file into multiple translation units), it probably 
means that it gravitates towards historical UNIX ref/def model and 
implements it as an extension.

> or
>
>     -- foo.h --
>     extern int foo;
>     extern int bar;
>     -- foo.c --
>     #include "foo.h"
>
>     int foo;
>     int bar = 1;
>     ...
>     -----------

This, on the other hand, is the proper way to do it under standard model.

> So, what do you usually use in such situations?

I prefer to write my code in standard C, unless I have a very very very 
good reason to do otherwise.

-- 
Best regards,
Andrey Tarasevich

[toc] | [prev] | [next] | [standalone]


#111704

Fromsupercat@casperkitty.com
Date2017-06-07 07:54 -0700
Message-ID<7b75c85b-8c3d-4716-aad4-ae452d719e95@googlegroups.com>
In reply to#111669
On Tuesday, June 6, 2017 at 8:37:23 PM UTC-5, Andrey Tarasevich wrote:
> On 6/6/2017 10:33 AM, Steve Keller wrote:
> >     -- foo.h --
> >     extern int foo;
> >     extern int bar;
> >     -- foo.c --
> >     #include "foo.h"
> >
> >     int foo;
> >     int bar = 1;
> >     ...
> >     -----------
> 
> This, on the other hand, is the proper way to do it under standard model.

Another variation is:

--foo.h
#ifdef FOO_C
#define varqual
#else
#define varqual extern
#endif
varqual int foo;

--foo.c
#define FOO_C
#include "foo.h"

This has the advantage of avoiding redundant declarations.  On compilers
which ignore initialization of extern qualifiers *except* for setting
array sizes, it has the useful feature of allowing things like:

  varqual int dat[] = {1,2,3,4,5};

in a .h file to allow all compilation modules to use the sizeof on dat
without having to manually compute the size.

[toc] | [prev] | [next] | [standalone]


#111707

FromDavid Brown <david.brown@hesbynett.no>
Date2017-06-07 17:23 +0200
Message-ID<oh95jj$19i$1@dont-email.me>
In reply to#111704
On 07/06/17 16:54, supercat@casperkitty.com wrote:
> On Tuesday, June 6, 2017 at 8:37:23 PM UTC-5, Andrey Tarasevich wrote:
>> On 6/6/2017 10:33 AM, Steve Keller wrote:
>>>      -- foo.h --
>>>      extern int foo;
>>>      extern int bar;
>>>      -- foo.c --
>>>      #include "foo.h"
>>>
>>>      int foo;
>>>      int bar = 1;
>>>      ...
>>>      -----------
>>
>> This, on the other hand, is the proper way to do it under standard model.
> 

Yes, that is is the way to do it.

> Another variation is:
> 
> --foo.h
> #ifdef FOO_C
> #define varqual
> #else
> #define varqual extern
> #endif
> varqual int foo;
> 
> --foo.c
> #define FOO_C
> #include "foo.h"
> 
> This has the advantage of avoiding redundant declarations.

This is, IMHO, a hideous way of exporting your variables.  I /really/ 
hate it when include files are not "stand alone" but depend on different 
values of #define'd macros, ordering of #include files, number of times 
included, etc.

Yes, people /do/ write this sort of thing - so it is worth knowing what 
it means.  But it is not a style I would want to see encouraged.

[toc] | [prev] | [next] | [standalone]


#111737

Fromsupercat@casperkitty.com
Date2017-06-07 11:10 -0700
Message-ID<8c067855-3441-4217-8465-06ec45778f60@googlegroups.com>
In reply to#111707
On Wednesday, June 7, 2017 at 10:24:00 AM UTC-5, David Brown wrote:
> On 07/06/17 16:54, supercat wrote:
> > Another variation is:
> > 
> > --foo.h
> > #ifdef FOO_C
> > #define varqual
> > #else
> > #define varqual extern
> > #endif
> > varqual int foo;
> > 
> > --foo.c
> > #define FOO_C
> > #include "foo.h"
> > 
> > This has the advantage of avoiding redundant declarations.
> 
> This is, IMHO, a hideous way of exporting your variables.  I /really/ 
> hate it when include files are not "stand alone" but depend on different 
> values of #define'd macros, ordering of #include files, number of times 
> included, etc.

The FOO_C macro would only be defined within FOO_C, and (while I forgot
to mention it) the varqual macro should be undef'ed at the end, or else
a module-specific name (FOO_VARQUAL) used instead.  The pattern avoids
the need to have the source text contain two separate declarations for
each variable that is shared between compilation units.  Further, in cases
where multiple modules will need to know the size of an exported table of
constants, I would think it much safer to have both the size and the
contents in the same (header) file, than to have the size in a header file
but the contents in a separate source file.  Given, e.g.

foo.h
    #define NUM_WIDGETS 4
    extern unsigned widgets[NUM_WIDGETS];
foo.c
    #include <foo.h>
    unsigned widgets[NUM_WIDGETS] = {1,2,3,4};

there would be no validation to ensure that foo.h and foo.c remain in sync.
If a version of foo.h which is intended for 5 widgets gets used with a
version of foo.c that only defines those four values, no diagnostic would
be produced, but everything including foo.c would think there were five
widgets numbered {1,2,3,4,0} rather than four.

[toc] | [prev] | [next] | [standalone]


#111753

FromDavid Brown <david.brown@hesbynett.no>
Date2017-06-07 22:13 +0200
Message-ID<oh9miv$um2$1@dont-email.me>
In reply to#111737
On 07/06/17 20:10, supercat@casperkitty.com wrote:
> On Wednesday, June 7, 2017 at 10:24:00 AM UTC-5, David Brown wrote:
>> On 07/06/17 16:54, supercat wrote:
>>> Another variation is:
>>>
>>> --foo.h
>>> #ifdef FOO_C
>>> #define varqual
>>> #else
>>> #define varqual extern
>>> #endif
>>> varqual int foo;
>>>
>>> --foo.c
>>> #define FOO_C
>>> #include "foo.h"
>>>
>>> This has the advantage of avoiding redundant declarations.
>>
>> This is, IMHO, a hideous way of exporting your variables.  I /really/
>> hate it when include files are not "stand alone" but depend on different
>> values of #define'd macros, ordering of #include files, number of times
>> included, etc.
> 
> The FOO_C macro would only be defined within FOO_C, and (while I forgot
> to mention it) the varqual macro should be undef'ed at the end, or else
> a module-specific name (FOO_VARQUAL) used instead.  The pattern avoids
> the need to have the source text contain two separate declarations for
> each variable that is shared between compilation units. 

If you think that is a problem, you have serious issues with your 
development practices.  Declare the exported symbol in the header, and 
define it in /one/ C file.  Make sure you include the header in the C 
file, and the compiler will tell you if they are out of synchronisation. 
  And if you are changing your global data so often that separate 
declarations and definitions are inconvenient, then your should scrap 
your program design and start again.

It is a /bad/ idea to make the same section of code have different 
effects at different times in the same program, due to pre-processor 
symbol definitions.  There are occasional uses, such as "x macros", but 
these should be used carefully and clearly.  It is far better if any 
given section of code in a header or a C file has the same meaning all 
the time in the given program build.

> Further, in cases
> where multiple modules will need to know the size of an exported table of
> constants, I would think it much safer to have both the size and the
> contents in the same (header) file, than to have the size in a header file
> but the contents in a separate source file.  

Nonsense.  How often might this be useful?  Once in a program?  Is that 
enough to spoil the rest of your code with a throwback from the nineties?

> Given, e.g.
> 
> foo.h
>      #define NUM_WIDGETS 4
>      extern unsigned widgets[NUM_WIDGETS];
> foo.c
>      #include <foo.h>
>      unsigned widgets[NUM_WIDGETS] = {1,2,3,4};
> 
> there would be no validation to ensure that foo.h and foo.c remain in sync.

First, you easily get validation against having too many initialisers. 
Checking against too few is harder - but easy to do during development 
(just add another, and see that you get a compile-time error).  Given 
that this is a sort of construct that you would rarely need in 
situations where you have so many initialisers that it is likely for you 
to make a mistake, it simply is not worth making an exception for it.

And if you really want to have compile-time checking of the number of 
initialisers, that is easy to do with a trick that works for any array 
initialisation (not just exported arrays):


#define WIDGETS_INIT { 1, 2, 3, 4 }
_Static_assert(sizeof((unsigned []) WIDGETS_INIT) == NUM_WIDGETS *
  			sizeof(unsigned), "Bad size");
unsigned widgets[] = WIDGETS_INIT;


> If a version of foo.h which is intended for 5 widgets gets used with a
> version of foo.c that only defines those four values, no diagnostic would
> be produced, but everything including foo.c would think there were five
> widgets numbered {1,2,3,4,0} rather than four.
> 

If you use a version of foo.h that is out of sync with foo.c like that, 
you have plenty of other problems to think about.

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c


csiph-web