Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #93934 > unrolled thread
| Started by | James Harris <james.harris.1@gmail.com> |
|---|---|
| First post | 2016-09-23 10:45 +0100 |
| Last post | 2016-09-23 11:37 -0400 |
| Articles | 20 on this page of 70 — 16 participants |
Back to article view | Back to comp.lang.c
To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-23 10:45 +0100
Re: To include or not to include David Brown <david.brown@hesbynett.no> - 2016-09-23 12:43 +0200
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-23 12:17 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-23 13:32 +0100
Re: To include or not to include David Brown <david.brown@hesbynett.no> - 2016-09-23 14:57 +0200
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-24 20:34 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-24 23:55 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 01:43 +0100
Re: To include or not to include Ian Collins <ian-news@hotmail.com> - 2016-09-25 14:38 +1300
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-25 11:42 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 11:58 +0100
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-25 14:06 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 15:15 +0100
Re: To include or not to include Reinhardt Behm <rbehm@hushmail.com> - 2016-09-25 23:33 +0800
Re: To include or not to include Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2016-09-25 10:04 -0600
Re: To include or not to include Reinhardt Behm <rbehm@hushmail.com> - 2016-09-26 00:21 +0800
Re: To include or not to include "James R. Kuyper" <jameskuyper@verizon.net> - 2016-09-25 13:17 -0400
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-26 00:18 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-26 17:55 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-26 20:07 +0100
Re: To include or not to include supercat@casperkitty.com - 2016-09-26 12:13 -0700
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-27 01:25 +0100
Re: To include or not to include supercat@casperkitty.com - 2016-09-26 18:05 -0700
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-27 14:27 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-27 14:49 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-27 15:15 +0100
Re: To include or not to include Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-09-27 07:42 -0700
Re: To include or not to include Keith Thompson <kst-u@mib.org> - 2016-09-27 09:30 -0700
Re: To include or not to include Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-09-27 10:06 -0700
Re: To include or not to include supercat@casperkitty.com - 2016-09-27 08:12 -0700
Re: To include or not to include Tim Rentsch <txr@alumni.caltech.edu> - 2016-09-28 10:46 -0700
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-27 17:55 +0100
Re: To include or not to include supercat@casperkitty.com - 2016-09-27 10:19 -0700
Re: To include or not to include Keith Thompson <kst-u@mib.org> - 2016-09-27 11:42 -0700
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-27 19:57 +0100
Re: To include or not to include Keith Thompson <kst-u@mib.org> - 2016-09-27 12:38 -0700
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-27 21:00 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-28 01:50 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-28 10:55 +0100
Re: To include or not to include Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-09-27 13:33 -0700
Re: To include or not to include Ike Naar <ike@iceland.freeshell.org> - 2016-09-28 19:13 +0000
Re: To include or not to include supercat@casperkitty.com - 2016-09-27 12:38 -0700
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-27 20:57 +0100
Re: To include or not to include supercat@casperkitty.com - 2016-09-27 07:25 -0700
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-28 00:28 +0100
Re: To include or not to include supercat@casperkitty.com - 2016-09-30 07:53 -0700
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-30 16:06 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-27 13:53 +0100
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-30 19:46 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-30 20:27 +0100
Re: To include or not to include Reinhardt Behm <rbehm@hushmail.com> - 2016-09-25 23:27 +0800
Re: To include or not to include Jerry Stuckle <jstucklex@attglobal.net> - 2016-09-25 11:38 -0400
Re: To include or not to include Reinhardt Behm <rbehm@hushmail.com> - 2016-09-25 23:40 +0800
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 15:06 +0100
Re: To include or not to include Jerry Stuckle <jstucklex@attglobal.net> - 2016-09-25 11:49 -0400
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 18:05 +0100
Re: To include or not to include Jerry Stuckle <jstucklex@attglobal.net> - 2016-09-25 16:42 -0400
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 21:53 +0100
Re: To include or not to include Jerry Stuckle <jstucklex@attglobal.net> - 2016-09-25 17:57 -0400
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-25 21:20 +0100
Re: To include or not to include BartC <bc@freeuk.com> - 2016-09-25 21:49 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-26 00:40 +0100
Re: To include or not to include David Brown <david.brown@hesbynett.no> - 2016-09-23 14:56 +0200
Re: To include or not to include Richard Heathfield <rjh@cpax.org.uk> - 2016-09-23 13:31 +0100
Re: To include or not to include James Harris <james.harris.1@gmail.com> - 2016-09-23 13:52 +0100
Re: To include or not to include Richard Heathfield <rjh@cpax.org.uk> - 2016-09-23 14:35 +0100
Re: To include or not to include Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-23 18:55 +0100
Re: To include or not to include Richard Heathfield <rjh@cpax.org.uk> - 2016-09-23 20:33 +0100
Re: To include or not to include Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2016-09-23 08:22 -0600
Re: To include or not to include Richard Damon <Richard@Damon-Family.org> - 2016-09-23 11:37 -0400
Page 1 of 4 [1] 2 3 4 Next page →
| From | James Harris <james.harris.1@gmail.com> |
|---|---|
| Date | 2016-09-23 10:45 +0100 |
| Subject | To include or not to include |
| Message-ID | <ns2tkb$p0$1@dont-email.me> |
I often have at the top of a C source file, which I'll call F, an include for its corresponding header: #include "F.h" After that come the various includes I need for the .c code that follows. In many cases the .c and the .h include some of the same files. They might both pull in stdio.h or string.h, for example. Of course, I could omit from F.c at least some of the headers which F.h includes. But I tend to take the approach of including in the .c all those the .c code needs. Assuming either approach is just as valid, which is the best style or is one more idiomatic than the other? As an aside, in F.c I include F.h so that the compiler can check that function definitions (in F.c) match function declarations (in F.h). So, if it's relevant, I guess I tend to use includes as a policy rather than as a mechanism for managing other includes. -- James Harris
[toc] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2016-09-23 12:43 +0200 |
| Message-ID | <ns310l$bj5$1@dont-email.me> |
| In reply to | #93934 |
On 23/09/16 11:45, James Harris wrote: > I often have at the top of a C source file, which I'll call F, an > include for its corresponding header: > > #include "F.h" > > After that come the various includes I need for the .c code that follows. > > In many cases the .c and the .h include some of the same files. They > might both pull in stdio.h or string.h, for example. > > Of course, I could omit from F.c at least some of the headers which F.h > includes. But I tend to take the approach of including in the .c all > those the .c code needs. > > Assuming either approach is just as valid, which is the best style or is > one more idiomatic than the other? > > > As an aside, in F.c I include F.h so that the compiler can check that > function definitions (in F.c) match function declarations (in F.h). So, > if it's relevant, I guess I tend to use includes as a policy rather than > as a mechanism for managing other includes. > I prefer the following rules for includes: 1. F.c should include F.h, to make sure that function definitions in F.c and function declarations in F.h match. 2. I make sure that any functions or file-level variables in F.c are either defined "static" in F.c, or they are declared "extern" in F.h and have matching definitions in F.c. 3. Header F.h should include all the headers it needs in order to be complete. If G.c includes F.h, it should not have to include anything else before or after it. So if F.h declares a function that takes a uint32_t, then F.h must include stdint.h. 4. Headers should have guards so that you can safely include them many times. 5. It should be possible to include the headers in any order (this mostly follows from 3 and 4), both in c and h files. 6. If F.c uses something from G.h, then it should include G.h - even if F.h already includes G.h. This is for clarity and self-documentation in the code. (Since this is not required for the correctness of the code, I guess you can count it as "policy rather than mechanism".) Things can get a lot more complicated with multiple directories, include paths, combined headers, etc. But I think they are good rules to start with.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-23 12:17 +0100 |
| Message-ID | <ns3316$i3b$1@dont-email.me> |
| In reply to | #93936 |
On 23/09/2016 11:43, David Brown wrote: > On 23/09/16 11:45, James Harris wrote: >> I often have at the top of a C source file, which I'll call F, an >> include for its corresponding header: >> >> #include "F.h" >> >> After that come the various includes I need for the .c code that follows. >> >> In many cases the .c and the .h include some of the same files. They >> might both pull in stdio.h or string.h, for example. >> >> Of course, I could omit from F.c at least some of the headers which F.h >> includes. But I tend to take the approach of including in the .c all >> those the .c code needs. >> >> Assuming either approach is just as valid, which is the best style or is >> one more idiomatic than the other? >> >> >> As an aside, in F.c I include F.h so that the compiler can check that >> function definitions (in F.c) match function declarations (in F.h). So, >> if it's relevant, I guess I tend to use includes as a policy rather than >> as a mechanism for managing other includes. >> > > I prefer the following rules for includes: > > 1. F.c should include F.h, to make sure that function definitions in F.c > and function declarations in F.h match. > > 2. I make sure that any functions or file-level variables in F.c are > either defined "static" in F.c, or they are declared "extern" in F.h and > have matching definitions in F.c. > > 3. Header F.h should include all the headers it needs in order to be > complete. If G.c includes F.h, it should not have to include anything > else before or after it. So if F.h declares a function that takes a > uint32_t, then F.h must include stdint.h. My rules would be a little different (using example module A): * A.h should only contain declarations for A.c that will be exported to or shared with other modules * A.h should not contain any includes that are not needed for A.h itself. Only includes that are necessary for A.h to compile as a standalone file. (For example, including a header that contains macros and typedefs used by A.h's declarations) The rules might be bent a little when looking at a whole project: * If a project P comprises modules A, B and C, then I might instead construct a composite header P.h containing A.h, B.h and C.h. * Then each of A.c, B.c and C.c includes P.h * Any external includes (outside of A, B and C) that are used by some or all of A, B and C, can also be placed in P.h. Then each module need only be headed by a single, common include file. It can get more complicated if A and B, for example, need to include each other's headers; which one is included first in each module? It may be necessary to extract items from A.h and B.h that are put into a third header (eg. AB.h) that is included before either A.h or B.h (or placed before them in P.h). C allows more elaborate interdependencies still, so it's not possible to have rigid rules, only guidelines. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-23 13:32 +0100 |
| Message-ID | <87zimyhk54.fsf@bsb.me.uk> |
| In reply to | #93937 |
BartC <bc@freeuk.com> writes: > On 23/09/2016 11:43, David Brown wrote: >> On 23/09/16 11:45, James Harris wrote: >>> I often have at the top of a C source file, which I'll call F, an >>> include for its corresponding header: >>> >>> #include "F.h" >>> >>> After that come the various includes I need for the .c code that follows. >>> >>> In many cases the .c and the .h include some of the same files. They >>> might both pull in stdio.h or string.h, for example. >>> >>> Of course, I could omit from F.c at least some of the headers which F.h >>> includes. But I tend to take the approach of including in the .c all >>> those the .c code needs. >>> >>> Assuming either approach is just as valid, which is the best style or is >>> one more idiomatic than the other? >>> >>> >>> As an aside, in F.c I include F.h so that the compiler can check that >>> function definitions (in F.c) match function declarations (in F.h). So, >>> if it's relevant, I guess I tend to use includes as a policy rather than >>> as a mechanism for managing other includes. >>> >> >> I prefer the following rules for includes: >> >> 1. F.c should include F.h, to make sure that function definitions in F.c >> and function declarations in F.h match. >> >> 2. I make sure that any functions or file-level variables in F.c are >> either defined "static" in F.c, or they are declared "extern" in F.h and >> have matching definitions in F.c. >> >> 3. Header F.h should include all the headers it needs in order to be >> complete. If G.c includes F.h, it should not have to include anything >> else before or after it. So if F.h declares a function that takes a >> uint32_t, then F.h must include stdint.h. > > My rules would be a little different (using example module A): > > * A.h should only contain declarations for A.c that will be exported to > or shared with other modules > > * A.h should not contain any includes that are not needed for A.h > itself. Only includes that are necessary for A.h to compile as a > standalone file. (For example, including a header that contains > macros and typedefs used by A.h's declarations) In what way is this different? Do you mean to imply that you *don't* make sure that A.c includes A.h? (That's the only thing I can see that is different, but I assumed it was an omission rather than a rule.) <snip> -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2016-09-23 14:57 +0200 |
| Message-ID | <ns38rb$5u6$2@dont-email.me> |
| In reply to | #93939 |
On 23/09/16 14:32, Ben Bacarisse wrote: > BartC <bc@freeuk.com> writes: > >> On 23/09/2016 11:43, David Brown wrote: >>> On 23/09/16 11:45, James Harris wrote: >>>> I often have at the top of a C source file, which I'll call F, an >>>> include for its corresponding header: >>>> >>>> #include "F.h" >>>> >>>> After that come the various includes I need for the .c code that follows. >>>> >>>> In many cases the .c and the .h include some of the same files. They >>>> might both pull in stdio.h or string.h, for example. >>>> >>>> Of course, I could omit from F.c at least some of the headers which F.h >>>> includes. But I tend to take the approach of including in the .c all >>>> those the .c code needs. >>>> >>>> Assuming either approach is just as valid, which is the best style or is >>>> one more idiomatic than the other? >>>> >>>> >>>> As an aside, in F.c I include F.h so that the compiler can check that >>>> function definitions (in F.c) match function declarations (in F.h). So, >>>> if it's relevant, I guess I tend to use includes as a policy rather than >>>> as a mechanism for managing other includes. >>>> >>> >>> I prefer the following rules for includes: >>> >>> 1. F.c should include F.h, to make sure that function definitions in F.c >>> and function declarations in F.h match. >>> >>> 2. I make sure that any functions or file-level variables in F.c are >>> either defined "static" in F.c, or they are declared "extern" in F.h and >>> have matching definitions in F.c. >>> >>> 3. Header F.h should include all the headers it needs in order to be >>> complete. If G.c includes F.h, it should not have to include anything >>> else before or after it. So if F.h declares a function that takes a >>> uint32_t, then F.h must include stdint.h. >> >> My rules would be a little different (using example module A): >> >> * A.h should only contain declarations for A.c that will be exported to >> or shared with other modules >> >> * A.h should not contain any includes that are not needed for A.h >> itself. Only includes that are necessary for A.h to compile as a >> standalone file. (For example, including a header that contains >> macros and typedefs used by A.h's declarations) > > In what way is this different? Do you mean to imply that you *don't* > make sure that A.c includes A.h? (That's the only thing I can see that > is different, but I assumed it was an omission rather than a rule.) > As I read it, my rule 3 says A.h should include everything it needs, while Bart has added that it should not include things it doesn't need.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-24 20:34 +0100 |
| Message-ID | <ns6kg9$eql$1@dont-email.me> |
| In reply to | #93939 |
On 23/09/2016 13:32, Ben Bacarisse wrote:
> BartC <bc@freeuk.com> writes:
>> On 23/09/2016 11:43, David Brown wrote:
>>> 1. F.c should include F.h, to make sure that function definitions in F.c
>>> and function declarations in F.h match.
>>>
>>> 2. I make sure that any functions or file-level variables in F.c are
>>> either defined "static" in F.c, or they are declared "extern" in F.h and
>>> have matching definitions in F.c.
>>>
>>> 3. Header F.h should include all the headers it needs in order to be
>>> complete. If G.c includes F.h, it should not have to include anything
>>> else before or after it. So if F.h declares a function that takes a
>>> uint32_t, then F.h must include stdint.h.
>>
>> My rules would be a little different (using example module A):
>>
>> * A.h should only contain declarations for A.c that will be exported to
>> or shared with other modules
>>
>> * A.h should not contain any includes that are not needed for A.h
>> itself. Only includes that are necessary for A.h to compile as a
>> standalone file. (For example, including a header that contains
>> macros and typedefs used by A.h's declarations)
>
> In what way is this different? Do you mean to imply that you *don't*
> make sure that A.c includes A.h? (That's the only thing I can see that
> is different, but I assumed it was an omission rather than a rule.)
I didn't see notice No. 2 rule. But I think my rules are stricter.
Looking at the problems that headers need to solve, they aren't actually
that straightforward. So given this set of names that are defined in
module A (local or exported in A, or imported elsewhere):
I_local Local int
J_shared Exported/Imported int
K_shared Exported/Imported int (initialised)
A_local Local array
B_shared Exported/Imported array
C_shared Exported/Imported array (initialised)
T_local Local typedef
U_shared Exported/Imported typedef
E_local Local enum and enum names
E_shared Exported/Imported enum and enum names
M_LOCAL Local macro
M_SHARED Exported/Imported macro
F_local Local function
G_shared Exported/Imported function
Then A's header A.h might be:
-----------------------
int J_shared;
int K_shared;
int B_shared[10];
int C_shared[];
typedef int U_shared;
enum {xx,yy,zz} E_shared;
#define M_SHARED
void G_shared(int);
-----------------------
while A.c would be:
-----------------------
#include "A.h"
static int I_local;
int K_shared = 1000;
static int A_local[10];
int C_shared[] = {10,20,30};
typedef int T_local;
enum {aa,bb,cc} E_local;
#define M_LOCAL
static void F_local(int);
static void F_local(int a){}
void G_shared(int a){}
-----------------------
Shared functions have to be declared twice (declaration and definition).
I choose to also have forward declarations for local functions.
Shared variables only need declaring once, unless they are initialised
then it's easier to declare them twice. (I don't use 'extern' on the
shared declaration unless forced to by a compiler or linker).
One funny thing about a header such as A.h is that it declares
*exported* names when compiled as part of A.c, but "imported" names must
be declared when compiled as part of any other module. All using exactly
the same syntax.
That's for variables and functions. With typedefs, enums and macros,
then each module harmlessly declares its own versions.
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-24 23:55 +0100 |
| Message-ID | <87a8ewgb5y.fsf@bsb.me.uk> |
| In reply to | #93990 |
BartC <bc@freeuk.com> writes: > On 23/09/2016 13:32, Ben Bacarisse wrote: >> BartC <bc@freeuk.com> writes: >>> On 23/09/2016 11:43, David Brown wrote: > >>>> 1. F.c should include F.h, to make sure that function definitions in F.c >>>> and function declarations in F.h match. >>>> >>>> 2. I make sure that any functions or file-level variables in F.c are >>>> either defined "static" in F.c, or they are declared "extern" in F.h and >>>> have matching definitions in F.c. >>>> >>>> 3. Header F.h should include all the headers it needs in order to be >>>> complete. If G.c includes F.h, it should not have to include anything >>>> else before or after it. So if F.h declares a function that takes a >>>> uint32_t, then F.h must include stdint.h. >>> >>> My rules would be a little different (using example module A): >>> >>> * A.h should only contain declarations for A.c that will be exported to >>> or shared with other modules >>> >>> * A.h should not contain any includes that are not needed for A.h >>> itself. Only includes that are necessary for A.h to compile as a >>> standalone file. (For example, including a header that contains >>> macros and typedefs used by A.h's declarations) >> >> In what way is this different? Do you mean to imply that you *don't* >> make sure that A.c includes A.h? (That's the only thing I can see that >> is different, but I assumed it was an omission rather than a rule.) > > I didn't see notice No. 2 rule. But I think my rules are stricter. > > Looking at the problems that headers need to solve, they aren't > actually that straightforward. So given this set of names that are > defined in module A (local or exported in A, or imported elsewhere): > > I_local Local int > J_shared Exported/Imported int > K_shared Exported/Imported int (initialised) > > A_local Local array > B_shared Exported/Imported array > C_shared Exported/Imported array (initialised) > > T_local Local typedef > U_shared Exported/Imported typedef > > E_local Local enum and enum names > E_shared Exported/Imported enum and enum names > > M_LOCAL Local macro > M_SHARED Exported/Imported macro > > F_local Local function > G_shared Exported/Imported function > > Then A's header A.h might be: > ----------------------- > int J_shared; > int K_shared; > > int B_shared[10]; > int C_shared[]; <snip> > Shared variables only need declaring once, unless they are initialised > then it's easier to declare them twice. (I don't use 'extern' on the > shared declaration unless forced to by a compiler or linker). That's an odd way of putting it. Alternatively you could say that you deliberately write undefined code but will correct it if the implementation happens to say something about it. Do you take the same view of any other cases of undefined behaviour? > One funny thing about a header such as A.h is that it declares > *exported* names when compiled as part of A.c, but "imported" names > must be declared when compiled as part of any other module. All using > exactly the same syntax. Another funny thing is that it provokes undefined behaviour (when used as expected). In effect, it's not really C. Why would you write it that way? -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-25 01:43 +0100 |
| Message-ID | <ns76j2$7eh$1@dont-email.me> |
| In reply to | #93998 |
On 24/09/2016 23:55, Ben Bacarisse wrote:
> BartC <bc@freeuk.com> writes:
>> Then A's header A.h might be:
>> -----------------------
>> int J_shared;
>> int K_shared;
>>
>> int B_shared[10];
>> int C_shared[];
> <snip>
>
>> Shared variables only need declaring once, unless they are initialised
>> then it's easier to declare them twice. (I don't use 'extern' on the
>> shared declaration unless forced to by a compiler or linker).
>
> That's an odd way of putting it. Alternatively you could say that you
> deliberately write undefined code but will correct it if the
> implementation happens to say something about it. Do you take the same
> view of any other cases of undefined behaviour?
What's undefined behaviour? Not using 'extern'?
I hardly ever use it (except in generated code; see below), and rarely
see it used elsewhere. But in my example, I had to use it with 'int
C_shared[]' when using Tiny C as that compiler didn't like the empty
array declaration.
>> One funny thing about a header such as A.h is that it declares
>> *exported* names when compiled as part of A.c, but "imported" names
>> must be declared when compiled as part of any other module. All using
>> exactly the same syntax.
>
> Another funny thing is that it provokes undefined behaviour (when used
> as expected). In effect, it's not really C. Why would you write it
> that way?
If this is still about 'extern', then it would seem odd writing this in A.h:
extern int C_shared[];
then a bit later in A.c and so within the same translation unit:
int C_shared[] = {10,20,30};
So declaring something as external, then defining the very same thing as
belonging to this module!
(With generated C code, I use a different approach that results in a
dedicated prelude for each generated C module, rather than explicit
shared headers.
Then A.c would see 'int C_shared[] = {10,20,30};', but X.c that imports
A, would see 'extern int C_shared[3];'. But here 'extern' is used in a
way that makes sense.)
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2016-09-25 14:38 +1300 |
| Message-ID | <e4oo4jFj4bkU1@mid.individual.net> |
| In reply to | #94006 |
On 09/25/16 01:43 PM, BartC wrote:
> If this is still about 'extern', then it would seem odd writing this in A.h:
>
> extern int C_shared[];
>
> then a bit later in A.c and so within the same translation unit:
>
> int C_shared[] = {10,20,30};
>
> So declaring something as external, then defining the very same thing as
> belonging to this module!
>
>
> (With generated C code, I use a different approach that results in a
> dedicated prelude for each generated C module, rather than explicit
> shared headers.
>
> Then A.c would see 'int C_shared[] = {10,20,30};', but X.c that imports
> A, would see 'extern int C_shared[3];'. But here 'extern' is used in a
> way that makes sense.)
Every compilation unit that includes A.h and uses C_shared has to see
the extern declaration, including the one with the definition.
--
Ian
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-25 11:42 +0100 |
| Message-ID | <87r388dzw9.fsf@bsb.me.uk> |
| In reply to | #94006 |
BartC <bc@freeuk.com> writes:
> On 24/09/2016 23:55, Ben Bacarisse wrote:
>> BartC <bc@freeuk.com> writes:
>
>>> Then A's header A.h might be:
>>> -----------------------
>>> int J_shared;
>>> int K_shared;
>>>
>>> int B_shared[10];
>>> int C_shared[];
>> <snip>
>>
>>> Shared variables only need declaring once, unless they are initialised
>>> then it's easier to declare them twice. (I don't use 'extern' on the
>>> shared declaration unless forced to by a compiler or linker).
>>
>> That's an odd way of putting it. Alternatively you could say that you
>> deliberately write undefined code but will correct it if the
>> implementation happens to say something about it. Do you take the same
>> view of any other cases of undefined behaviour?
>
> What's undefined behaviour? Not using 'extern'?
Not exactly. It's having multiple definitions for the some identifier
with external linkage. extern can be used to prevent that.
> I hardly ever use it (except in generated code; see below), and rarely
> see it used elsewhere.
I'd hope not. After decades of education, I would hope that programs
that rely on "global" data objects are getting increasingly rare. If
you mean that don't see it used when it should be used, that's another
matter.
> But in my example, I had to use it with 'int
> C_shared[]' when using Tiny C as that compiler didn't like the empty
> array declaration.
It's a tentative *defintiion* with an incomplete type. At the end of
the translation unit it should be processed as if it had been written
int C_shared[] = {0};
>>> One funny thing about a header such as A.h is that it declares
>>> *exported* names when compiled as part of A.c, but "imported" names
>>> must be declared when compiled as part of any other module. All using
>>> exactly the same syntax.
>>
>> Another funny thing is that it provokes undefined behaviour (when used
>> as expected). In effect, it's not really C. Why would you write it
>> that way?
>
> If this is still about 'extern', then it would seem odd writing this in A.h:
>
> extern int C_shared[];
>
> then a bit later in A.c and so within the same translation unit:
>
> int C_shared[] = {10,20,30};
>
> So declaring something as external, then defining the very same thing
> as belonging to this module!
What a joy for you. C has the power to amaze you with some new oddity
every day.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-25 11:58 +0100 |
| Message-ID | <ns8akc$o3j$1@dont-email.me> |
| In reply to | #94017 |
On 25/09/2016 11:42, Ben Bacarisse wrote:
> BartC <bc@freeuk.com> writes:
>> If this is still about 'extern', then it would seem odd writing this in A.h:
>>
>> extern int C_shared[];
>>
>> then a bit later in A.c and so within the same translation unit:
>>
>> int C_shared[] = {10,20,30};
>>
>> So declaring something as external, then defining the very same thing
>> as belonging to this module!
>
> What a joy for you. C has the power to amaze you with some new oddity
> every day.
It's not just about me. It's illogical and confusing. If I write this in
Nasm:
extern bart
bart:
it says symbol 'bart' is redefined.
I thought C was supposed to be higher level than such languages?
The C method does solve one problem (having a single form of declaration
that works in both exporting and importing modules, rather than needing
two), but it is not satisfactory.
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@gmail.com> |
|---|---|
| Date | 2016-09-25 14:06 +0100 |
| Message-ID | <ns8i3i$fks$1@dont-email.me> |
| In reply to | #94020 |
On 25/09/2016 11:58, BartC wrote:
> On 25/09/2016 11:42, Ben Bacarisse wrote:
>> BartC <bc@freeuk.com> writes:
>
>>> If this is still about 'extern', then it would seem odd writing this
>>> in A.h:
>>>
>>> extern int C_shared[];
>>>
>>> then a bit later in A.c and so within the same translation unit:
>>>
>>> int C_shared[] = {10,20,30};
>>>
>>> So declaring something as external, then defining the very same thing
>>> as belonging to this module!
>>
>> What a joy for you. C has the power to amaze you with some new oddity
>> every day.
>
> It's not just about me. It's illogical and confusing. If I write this in
> Nasm:
>
> extern bart
> bart:
>
> it says symbol 'bart' is redefined.
>
> I thought C was supposed to be higher level than such languages?
Some of C's rules are for compatibility.
Besides, are you interpreting the extern keyword incorrectly. It does
not mean what it does in Nasm. IIRC it does not mean "external to this
translation unit".
Wouldn't it be more accurate to say that it means "external to a function"?
> The C method does solve one problem (having a single form of declaration
> that works in both exporting and importing modules, rather than needing
> two), but it is not satisfactory.
I think the issue is that extern declarations are often placed in header
files. It is convenient for the same headers to be #included in the
clients as well as the provider.
For example, imagine you had a program fred.c with a fred.h header and
fred.c had a global (i.e. exported) symbol mary. Then, fred.h would have
extern int mary ....
That header is also #included in fred.c so the compiler could see
int mary ....
extern int mary ....
C allows them both to appear in the same TU. Perhaps at the file level
extern could be seen as a sort of tentative declaration....?
--
James Harris
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-25 15:15 +0100 |
| Message-ID | <ns8m5r$sqi$1@dont-email.me> |
| In reply to | #94023 |
On 25/09/2016 14:06, James Harris wrote: > On 25/09/2016 11:58, BartC wrote: >> It's not just about me. It's illogical and confusing. If I write this in >> Nasm: >> >> extern bart >> bart: >> >> it says symbol 'bart' is redefined. >> >> I thought C was supposed to be higher level than such languages? > > Some of C's rules are for compatibility. > > Besides, are you interpreting the extern keyword incorrectly. It does > not mean what it does in Nasm. IIRC it does not mean "external to this > translation unit". > > Wouldn't it be more accurate to say that it means "external to a function"? Yes, I remember now that this came up before; extern means external to a function (and a new entry for my list of quirks!). In which case it's surprising you don't see it more often, in front of function declarations definitions for example. The answer is that most such 'extern' names are obviously external-to-functions by context (ie. placement). So it seems superfluous. Unless, for some bizarre reason, you want to actually declare a file-scope name inside a function. And it doesn't really explain however why 'extern' needs to be used for names of file-scope objects and functions that are shared between modules. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | Reinhardt Behm <rbehm@hushmail.com> |
|---|---|
| Date | 2016-09-25 23:33 +0800 |
| Message-ID | <ns8qnt$c3b$1@dont-email.me> |
| In reply to | #94025 |
AT Sunday 25 September 2016 22:15, BartC wrote: > On 25/09/2016 14:06, James Harris wrote: >> On 25/09/2016 11:58, BartC wrote: > >>> It's not just about me. It's illogical and confusing. If I write this in >>> Nasm: >>> >>> extern bart >>> bart: >>> >>> it says symbol 'bart' is redefined. >>> >>> I thought C was supposed to be higher level than such languages? >> >> Some of C's rules are for compatibility. >> >> Besides, are you interpreting the extern keyword incorrectly. It does >> not mean what it does in Nasm. IIRC it does not mean "external to this >> translation unit". >> >> Wouldn't it be more accurate to say that it means "external to a >> function"? > > Yes, I remember now that this came up before; extern means external to a > function (and a new entry for my list of quirks!). > > In which case it's surprising you don't see it more often, in front of > function declarations definitions for example. The answer is that most > such 'extern' names are obviously external-to-functions by context (ie. > placement). > > So it seems superfluous. Unless, for some bizarre reason, you want to > actually declare a file-scope name inside a function. > > And it doesn't really explain however why 'extern' needs to be used for > names of file-scope objects and functions that are shared between modules. The interpretation "extern to a function" does not make sense. A static declaration in file scope is also "extern to a function" but not known outside the compilation unit. -- Reinhardt
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2016-09-25 10:04 -0600 |
| Message-ID | <1b7fa0kltw.fsf@pfeifferfamily.net> |
| In reply to | #94027 |
Reinhardt Behm <rbehm@hushmail.com> writes:
> AT Sunday 25 September 2016 22:15, BartC wrote:
>
>> On 25/09/2016 14:06, James Harris wrote:
>>> On 25/09/2016 11:58, BartC wrote:
>>
>>>> It's not just about me. It's illogical and confusing. If I write this in
>>>> Nasm:
>>>>
>>>> extern bart
>>>> bart:
>>>>
>>>> it says symbol 'bart' is redefined.
>>>>
>>>> I thought C was supposed to be higher level than such languages?
>>>
>>> Some of C's rules are for compatibility.
>>>
>>> Besides, are you interpreting the extern keyword incorrectly. It does
>>> not mean what it does in Nasm. IIRC it does not mean "external to this
>>> translation unit".
>>>
>>> Wouldn't it be more accurate to say that it means "external to a
>>> function"?
>>
>> Yes, I remember now that this came up before; extern means external to a
>> function (and a new entry for my list of quirks!).
>>
>> In which case it's surprising you don't see it more often, in front of
>> function declarations definitions for example. The answer is that most
>> such 'extern' names are obviously external-to-functions by context (ie.
>> placement).
>>
>> So it seems superfluous. Unless, for some bizarre reason, you want to
>> actually declare a file-scope name inside a function.
>>
>> And it doesn't really explain however why 'extern' needs to be used for
>> names of file-scope objects and functions that are shared between modules.
>
> The interpretation "extern to a function" does not make sense. A static
> declaration in file scope is also "extern to a function" but not known
> outside the compilation unit.
It may not make sense, but here's what the standard says (6.9, point 4):
As discussed in 5.1.1.1, the unit of program text after
preprocessing is a translation unit, which consists of a sequence of
external declarations. These are described as "external" because
they appear outside any function (and hence have file scope).
[toc] | [prev] | [next] | [standalone]
| From | Reinhardt Behm <rbehm@hushmail.com> |
|---|---|
| Date | 2016-09-26 00:21 +0800 |
| Message-ID | <ns8thv$jj3$3@dont-email.me> |
| In reply to | #94033 |
AT Monday 26 September 2016 00:04, Joe Pfeiffer wrote: > Reinhardt Behm <rbehm@hushmail.com> writes: > >> AT Sunday 25 September 2016 22:15, BartC wrote: >> >>> On 25/09/2016 14:06, James Harris wrote: >>>> On 25/09/2016 11:58, BartC wrote: >>> >>>>> It's not just about me. It's illogical and confusing. If I write this >>>>> in Nasm: >>>>> >>>>> extern bart >>>>> bart: >>>>> >>>>> it says symbol 'bart' is redefined. >>>>> >>>>> I thought C was supposed to be higher level than such languages? >>>> >>>> Some of C's rules are for compatibility. >>>> >>>> Besides, are you interpreting the extern keyword incorrectly. It does >>>> not mean what it does in Nasm. IIRC it does not mean "external to this >>>> translation unit". >>>> >>>> Wouldn't it be more accurate to say that it means "external to a >>>> function"? >>> >>> Yes, I remember now that this came up before; extern means external to a >>> function (and a new entry for my list of quirks!). >>> >>> In which case it's surprising you don't see it more often, in front of >>> function declarations definitions for example. The answer is that most >>> such 'extern' names are obviously external-to-functions by context (ie. >>> placement). >>> >>> So it seems superfluous. Unless, for some bizarre reason, you want to >>> actually declare a file-scope name inside a function. >>> >>> And it doesn't really explain however why 'extern' needs to be used for >>> names of file-scope objects and functions that are shared between >>> modules. >> >> The interpretation "extern to a function" does not make sense. A static >> declaration in file scope is also "extern to a function" but not known >> outside the compilation unit. > > It may not make sense, but here's what the standard says (6.9, point 4): > As discussed in 5.1.1.1, the unit of program text after > preprocessing is a translation unit, which consists of a sequence of > external declarations. These are described as "external" because > they appear outside any function (and hence have file scope). It may be the word (not even that it's "external" against "extern") but this has not much to do with what has been discussed her. -- Reinhardt
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-09-25 13:17 -0400 |
| Message-ID | <f923c0cc-7729-e4b9-1673-f9afa6ef19ca@verizon.net> |
| In reply to | #94033 |
On 09/25/2016 12:04 PM, Joe Pfeiffer wrote: ... > It may not make sense, but here's what the standard says (6.9, point 4): > As discussed in 5.1.1.1, the unit of program text after > preprocessing is a translation unit, which consists of a sequence of > external declarations. These are described as "external" because > they appear outside any function (and hence have file scope). The word "external" in "external declarations" is not directly connected to the same word when used in the term "external linkage". Anything with external linkage must have an external declaration, but not all external declarations have external linkage. This is another case where C's jargon is less than ideal.
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@gmail.com> |
|---|---|
| Date | 2016-09-26 00:18 +0100 |
| Message-ID | <ns9m0h$fij$1@dont-email.me> |
| In reply to | #94025 |
On 25/09/2016 15:15, BartC wrote:
> On 25/09/2016 14:06, James Harris wrote:
>> On 25/09/2016 11:58, BartC wrote:
>
>>> It's not just about me. It's illogical and confusing. If I write this in
>>> Nasm:
>>>
>>> extern bart
>>> bart:
>>>
>>> it says symbol 'bart' is redefined.
>>>
>>> I thought C was supposed to be higher level than such languages?
>>
>> Some of C's rules are for compatibility.
>>
>> Besides, are you interpreting the extern keyword incorrectly. It does
>> not mean what it does in Nasm. IIRC it does not mean "external to this
>> translation unit".
>>
>> Wouldn't it be more accurate to say that it means "external to a
>> function"?
>
> Yes, I remember now that this came up before; extern means external to a
> function (and a new entry for my list of quirks!).
>
> In which case it's surprising you don't see it more often, in front of
> function declarations definitions for example. The answer is that most
> such 'extern' names are obviously external-to-functions by context (ie.
> placement).
>
> So it seems superfluous. Unless, for some bizarre reason, you want to
> actually declare a file-scope name inside a function.
IIRC this goes back to support for single pass. Consider this code:
int x = 1;
int f(void) {
return x;
}
When the compiler gets to see x in the function it knows its type.
Now imagine that the definition of x is placed after f(). In that case,
extern would be needed, as in
int f(void) {
extern int x;
return x;
}
int x = 1;
In this case, the extern tells the compiler the type of x. If it did not
then it would not know the type when it needed to.
Because we normally declare external variables at file scope /before/
functions we don't normally need such uses of extern.
> And it doesn't really explain however why 'extern' needs to be used for
> names of file-scope objects and functions that are shared between modules.
From memory I think this is a subtle difference between declarations
(i.e. setting type information) and definitions (reserving storage).
Consider at file scope
int x;
Even though that does not assign a value, IIRC the compiler might treat
it as a definition (rules as to when are fiddly). But compare that with
extern int x;
The compiler will always see that as a declaration (again, with very
fiddly rules) and not a definition.
Oversimplification: Maybe extern declares without reserving storage,
even at file scope.
I fully expect to be criticised mercilessly for the above description...!
--
James Harris
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-26 17:55 +0100 |
| Message-ID | <nsbju0$4vl$1@dont-email.me> |
| In reply to | #94063 |
On 26/09/2016 00:18, James Harris wrote:
> On 25/09/2016 15:15, BartC wrote:
>> So it seems superfluous. Unless, for some bizarre reason, you want to
>> actually declare a file-scope name inside a function.
>
> IIRC this goes back to support for single pass. Consider this code:
>
> int x = 1;
> int f(void) {
> return x;
> }
>
> When the compiler gets to see x in the function it knows its type.
Presumably single passes are still used because C still has trouble with
references to functions and variables that occur later on. (Or more
likely, it has to maintain that behaviour even though forward
declaration requirements could easily be relaxed.)
>> And it doesn't really explain however why 'extern' needs to be used for
>> names of file-scope objects and functions that are shared between
>> modules.
>
> From memory I think this is a subtle difference between declarations
> (i.e. setting type information) and definitions (reserving storage).
> Consider at file scope
>
> int x;
>
> Even though that does not assign a value, IIRC the compiler might treat
> it as a definition (rules as to when are fiddly). But compare that with
>
> extern int x;
>
> The compiler will always see that as a declaration (again, with very
> fiddly rules) and not a definition.
All C compilers I've tried have no problem with:
int x;
int x;
int x;
int x;
int x;
within the same module, provided only one is initialised.
Even when some of these are in other modules: the linker sees multiple
instances and creates only one. EXCEPT when I use Tiny C, then it will
report multiple definitions of x. That can be fixed by using extern in
all either modules except one or even when extern is used everywhere.
I don't know if that's a problem with Tiny C, or if it's all the other
compilers that are being lax.
> Oversimplification: Maybe extern declares without reserving storage,
> even at file scope.
But as I said, you can use only extern, and it will still reserve storage.
> I fully expect to be criticised mercilessly for the above description...!
Actually I don't think even the C experts know exactly how it works half
the time!
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-26 20:07 +0100 |
| Message-ID | <87k2dycweu.fsf@bsb.me.uk> |
| In reply to | #94095 |
BartC <bc@freeuk.com> writes:
> On 26/09/2016 00:18, James Harris wrote:
>> On 25/09/2016 15:15, BartC wrote:
>
>>> So it seems superfluous. Unless, for some bizarre reason, you want to
>>> actually declare a file-scope name inside a function.
>>
>> IIRC this goes back to support for single pass. Consider this code:
>>
>> int x = 1;
>> int f(void) {
>> return x;
>> }
>>
>> When the compiler gets to see x in the function it knows its type.
>
> Presumably single passes are still used because C still has trouble
> with references to functions and variables that occur later on. (Or
> more likely, it has to maintain that behaviour even though forward
> declaration requirements could easily be relaxed.)
>
>>> And it doesn't really explain however why 'extern' needs to be used for
>>> names of file-scope objects and functions that are shared between
>>> modules.
>>
>> From memory I think this is a subtle difference between declarations
>> (i.e. setting type information) and definitions (reserving storage).
>> Consider at file scope
>>
>> int x;
>>
>> Even though that does not assign a value, IIRC the compiler might treat
>> it as a definition (rules as to when are fiddly). But compare that with
>>
>> extern int x;
>>
>> The compiler will always see that as a declaration (again, with very
>> fiddly rules) and not a definition.
>
> All C compilers I've tried have no problem with:
>
> int x;
> int x;
> int x;
> int x;
> int x;
All conforming C compilers must have no problem with this.
> within the same module, provided only one is initialised.
I'd say "at file scope in the same translation unit, provided no more
than one is initialised". These are all what are call tentative
definitions. The syntax is there because of the long, deep history of
C. I doubt anyone would design it this way if starting from scratch.
> Even when some of these are in other modules: the linker sees multiple
> instances and creates only one.
If some appear in other translation units (C does not have modules), the
result is undefined (as I said before).
> EXCEPT when I use Tiny C, then it will
> report multiple definitions of x. That can be fixed by using extern in
> all either modules except one or even when extern is used everywhere.
Using extern everywhere fixes nothing -- it only "works" if the object
is not used since it prevents there being any definition of it.
> I don't know if that's a problem with Tiny C, or if it's all the other
> compilers that are being lax.
With gcc you are, implicitly, asking for this behaviour because gcc's
default behaviour is to place such objects into a common block. You can
stop this by using -fno-common, in which case the linker will complain
about the multiple definitions. All of these behaviours are conforming.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
Page 1 of 4 [1] 2 3 4 Next page →
Back to top | Article view | comp.lang.c
csiph-web