Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #397206 > unrolled thread
| Started by | Bart <bc@freeuk.com> |
|---|---|
| First post | 2026-03-26 22:36 +0000 |
| Last post | 2026-03-30 00:41 -0700 |
| Articles | 20 on this page of 66 — 10 participants |
Back to article view | Back to comp.lang.c
gcc and 'include' Bart <bc@freeuk.com> - 2026-03-26 22:36 +0000
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-26 16:12 -0700
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-27 10:55 +0000
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-27 13:49 +0100
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-27 10:51 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-04-25 10:09 -0700
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-04-25 15:58 -0700
Re: gcc and 'include' Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-03-27 21:27 +0000
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-27 22:05 +0000
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-27 17:03 -0700
Re: gcc and 'include' Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2026-03-28 05:10 +0100
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-28 20:37 +0300
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-28 18:33 +0000
Re: gcc and 'include' antispam@fricas.org (Waldek Hebisch) - 2026-03-29 00:53 +0000
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-29 22:37 +0100
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 05:33 -0700
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-30 14:42 +0100
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-30 16:53 +0300
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-30 18:11 +0100
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 08:27 -0700
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-30 11:54 -0700
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-30 21:54 +0100
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-30 18:07 -0700
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-31 11:39 +0100
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-03-31 13:56 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-04-06 20:56 -0700
Re: gcc and 'include' Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-04-06 23:12 -0700
Re: gcc and 'include' James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-03-30 21:06 -0400
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-29 11:24 +0200
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-29 12:44 +0100
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-31 15:57 +0200
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 07:20 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 05:07 -0700
Re: gcc and 'include' Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-03-27 00:25 +0000
Re: gcc and 'include' Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-03-30 07:13 +0000
Re: gcc and 'include' Andrey Tarasevich <noone@noone.net> - 2026-03-30 07:54 -0700
Re: gcc and 'include' Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-03-31 01:46 +0000
Re: gcc and 'include' antispam@fricas.org (Waldek Hebisch) - 2026-03-31 05:28 +0000
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-27 04:10 +0300
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-26 19:08 -0700
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-27 16:47 +0300
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-27 16:43 +0100
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-27 09:03 -0700
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-29 11:46 +0300
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 08:19 -0700
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-30 20:08 +0300
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-31 00:26 -0700
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-31 11:27 +0300
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-04-07 09:45 -0700
Re: gcc and 'include' Andrey Tarasevich <noone@noone.net> - 2026-03-28 10:25 -0700
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-29 10:37 +0300
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-29 11:30 +0200
Re: gcc and 'include' Andrey Tarasevich <noone@noone.net> - 2026-03-29 07:22 -0700
Re: gcc and 'include' James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-03-29 13:56 -0400
Re: gcc and 'include' Michael S <already5chosen@yahoo.com> - 2026-03-29 21:39 +0300
Re: gcc and 'include' James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-03-29 20:08 -0400
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-30 01:58 +0100
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 07:59 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-26 19:06 -0700
Re: gcc and 'include' Bart <bc@freeuk.com> - 2026-03-27 16:20 +0000
Re: gcc and 'include' David Brown <david.brown@hesbynett.no> - 2026-03-27 18:07 +0100
Re: gcc and 'include' James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-03-28 18:48 -0400
Re: gcc and 'include' Andrey Tarasevich <noone@noone.net> - 2026-03-27 22:38 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-28 00:30 -0700
Re: gcc and 'include' Andrey Tarasevich <noone@noone.net> - 2026-03-29 16:15 -0700
Re: gcc and 'include' Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-30 00:41 -0700
Page 1 of 4 [1] 2 3 4 Next page →
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-26 22:36 +0000 |
| Subject | gcc and 'include' |
| Message-ID | <10q4ceb$38i2d$1@dont-email.me> |
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find a
function 'F'.
Is it supposed to behave like that? I assume no discrete function F is
being generated because of the 'inline' attribute, but you'd think it
would ignore that if inlining wasn't enabled.
It compiles fine with or without 'inline' using tcc and bcc (my product).
Clang fails too, but clearly only because it has to duplicate gcc's
behaviour for drop-in compatability, even if it is crass.
(This is the actual program:
https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
[toc] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-03-26 16:12 -0700 |
| Message-ID | <87ikaiw5g0.fsf@example.invalid> |
| In reply to | #397206 |
Bart <bc@freeuk.com> writes:
> Take this program:
>
> #include <stdlib.h>
>
> inline int F(){return rand();}
>
> int main(void) {
> return F();
> }
>
> This compiles fine with gcc using -O1 -O2 -O3.
>
> But compile without optimising, and it fails to link as it can't find
> a function 'F'.
>
> Is it supposed to behave like that? I assume no discrete function F is
> being generated because of the 'inline' attribute, but you'd think it
> would ignore that if inlining wasn't enabled.
>
> It compiles fine with or without 'inline' using tcc and bcc (my product).
>
> Clang fails too, but clearly only because it has to duplicate gcc's
> behaviour for drop-in compatability, even if it is crass.
>
> (This is the actual program:
> https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
I don't have an answer to your question, but defining F as
"static inline" makes the program work. You can also do this:
inline int F();
int F(){return rand();}
(Unless you're using a C23 compiler, I suggest "int F(void)" rather
than "int F()".)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-27 10:55 +0000 |
| Message-ID | <10q5nnr$3l3lc$1@dont-email.me> |
| In reply to | #397207 |
On 26/03/2026 23:12, Keith Thompson wrote:
> Bart <bc@freeuk.com> writes:
>> Take this program:
>>
>> #include <stdlib.h>
>>
>> inline int F(){return rand();}
>>
>> int main(void) {
>> return F();
>> }
>>
>> This compiles fine with gcc using -O1 -O2 -O3.
>>
>> But compile without optimising, and it fails to link as it can't find
>> a function 'F'.
>>
>> Is it supposed to behave like that? I assume no discrete function F is
>> being generated because of the 'inline' attribute, but you'd think it
>> would ignore that if inlining wasn't enabled.
>>
>> It compiles fine with or without 'inline' using tcc and bcc (my product).
>>
>> Clang fails too, but clearly only because it has to duplicate gcc's
>> behaviour for drop-in compatability, even if it is crass.
>>
>> (This is the actual program:
>> https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
>
> I don't have an answer to your question, but defining F as
> "static inline" makes the program work. You can also do this:
>
> inline int F();
> int F(){return rand();}
Using 'static' to force an actual function to be generated sounds
unintuitive. It is when F is exported to be called from another module
that a tangible F function needs to exist.
But my example came from some pre-existing legacy code, where I was
surprised that -O0 made it fail.
> (Unless you're using a C23 compiler, I suggest "int F(void)" rather
> than "int F()".)
Nobody bothers with that any more. Most seem to assume that () already
means zero parameters anyway, judging by the incorrect usage I
constantly saw in open source code.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2026-03-27 13:49 +0100 |
| Message-ID | <10q5ud7$3np50$1@dont-email.me> |
| In reply to | #397221 |
On 27/03/2026 11:55, Bart wrote:
> On 26/03/2026 23:12, Keith Thompson wrote:
>> Bart <bc@freeuk.com> writes:
>>> Take this program:
>>>
>>> #include <stdlib.h>
>>>
>>> inline int F(){return rand();}
>>>
>>> int main(void) {
>>> return F();
>>> }
>>>
>>> This compiles fine with gcc using -O1 -O2 -O3.
>>>
>>> But compile without optimising, and it fails to link as it can't find
>>> a function 'F'.
>>>
>>> Is it supposed to behave like that? I assume no discrete function F is
>>> being generated because of the 'inline' attribute, but you'd think it
>>> would ignore that if inlining wasn't enabled.
>>>
>>> It compiles fine with or without 'inline' using tcc and bcc (my
>>> product).
>>>
>>> Clang fails too, but clearly only because it has to duplicate gcc's
>>> behaviour for drop-in compatability, even if it is crass.
>>>
>>> (This is the actual program:
>>> https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
>>
>> I don't have an answer to your question, but defining F as
>> "static inline" makes the program work. You can also do this:
>>
>> inline int F();
>> int F(){return rand();}
>
>
> Using 'static' to force an actual function to be generated sounds
> unintuitive. It is when F is exported to be called from another module
> that a tangible F function needs to exist.
>
> But my example came from some pre-existing legacy code, where I was
> surprised that -O0 made it fail.
>
I agree with you that this behaviour can be surprising, but it is
correct according to the C standards. "inline" alone in C means that
the compiler /may/ use the inline definition in code generation - but it
may also use a normal external linkage definition found at link time.
The inline definition and the external definition do not have to be the
same - it is fine to have a local inline version in a file that is
different from the external one.
For example, you could have a normal function in your program called
"sort" that can sort arrays of any size. But you know that in this one
particular C file, you only ever need to sort arrays of size 4. You
write a specialised version - keeping the same name "sort" and the same
function signature, and mark it "inline". The compiler can choose
either version when generating the code - the local inline version, or
the external version. Typically you get the local version when
optimising. (The compiler can always choose the local version even when
you have used a flag like "-O0" - the C standard does not care about
optimisation.)
But this all also means that even when you have a version of the
function locally with "inline", the compiler can choose to generate code
that refers to an externally linked version. This is the normal
behaviour for compilers when you are not optimising (though again the
standard does not require this). A common reason for not optimising
code is because you want to debug it, and that can be easier with a
single external version of the function - perhaps you want to put a
breakpoint in it when using a debugger, or add printf statements, or
whatever.
Another source of confusion here is that this is all subtly different
from C++'s "inline", as well as the "gnu89" "inline" that gcc supported
as an extension before C99 was published. In both these cases, the
local "inline" function will be used (whether it is actually generated
inline or as a function call is up to the compiler optimisation). But
you are free to have an external version of the function as well,
without conflict.
Generally, as Keith suggested, it is best to use "static inline" rather
than plain "inline". Then the code is logically a static function -
callers will always use the local copy, and nothing is exported. You
can also use "extern inline" which marks the function as being available
for local inlining, and the definition is also available for external
linkage. But "static inline" is by far the most common. Both "static
inline" and "extern inline" work the same way for C99 inline, gnu89
inline and C++ inline.
>> (Unless you're using a C23 compiler, I suggest "int F(void)" rather
>> than "int F()".)
>
>
> Nobody bothers with that any more. Most seem to assume that () already
> means zero parameters anyway, judging by the incorrect usage I
> constantly saw in open source code.
>
I think almost all C programmers write "int foo(void);", not "int
foo();". The exception is primarily people who are used to writing C++
rather than C. You can omit the "void" in the definition of a function
with no parameters, but I don't see any good reason to do that.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-03-27 10:51 -0700 |
| Message-ID | <87pl4pnotc.fsf@example.invalid> |
| In reply to | #397221 |
Bart <bc@freeuk.com> writes:
> On 26/03/2026 23:12, Keith Thompson wrote:
[...]
>> (Unless you're using a C23 compiler, I suggest "int F(void)" rather
>> than "int F()".)
>
> Nobody bothers with that any more.
I presume that's meant to be hyperbole. Plenty of C programmers do
bother with that.
> Most seem to assume that () already
> means zero parameters anyway, judging by the incorrect usage I
> constantly saw in open source code.
I find it better to write correct code than to look for excuses
to write poor code. If I define a parameterless function F, I
absolutely want a diagnostic if I call it with one or more arguments.
If nothing else, it's an opportunity to set a good example.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-04-25 10:09 -0700 |
| Message-ID | <86wlxv2chg.fsf@linuxsc.com> |
| In reply to | #397233 |
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: > Bart <bc@freeuk.com> writes: > >> On 26/03/2026 23:12, Keith Thompson wrote: > > [...] > >>> (Unless you're using a C23 compiler, I suggest "int F(void)" rather >>> than "int F()".) >> >> Nobody bothers with that any more. > > I presume that's meant to be hyperbole. Plenty of C programmers do > bother with that. > >> Most seem to assume that () already >> means zero parameters anyway, judging by the incorrect usage I >> constantly saw in open source code. > > I find it better to write correct code than to look for excuses > to write poor code. If I define a parameterless function F, I > absolutely want a diagnostic if I call it with one or more arguments. > If nothing else, it's an opportunity to set a good example. The declaration "int F();" is correct code. Just because you don't like it doesn't mean it's wrong.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-04-25 15:58 -0700 |
| Message-ID | <10sjguu$1668o$7@kst.eternal-september.org> |
| In reply to | #397941 |
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
> Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
>> Bart <bc@freeuk.com> writes:
>>> On 26/03/2026 23:12, Keith Thompson wrote:
>> [...]
>>
>>>> (Unless you're using a C23 compiler, I suggest "int F(void)" rather
>>>> than "int F()".)
>>>
>>> Nobody bothers with that any more.
>>
>> I presume that's meant to be hyperbole. Plenty of C programmers do
>> bother with that.
>>
>>> Most seem to assume that () already
>>> means zero parameters anyway, judging by the incorrect usage I
>>> constantly saw in open source code.
>>
>> I find it better to write correct code than to look for excuses
>> to write poor code. If I define a parameterless function F, I
>> absolutely want a diagnostic if I call it with one or more arguments.
>> If nothing else, it's an opportunity to set a good example.
>
> The declaration "int F();" is correct code. Just because you
> don't like it doesn't mean it's wrong.
You're right. I see that I unintentionally implied that it's wrong.
I should have written "good code" rather than "correct code".
"int F()" is correct code. For pre-C23 versions of C, it is in my
opinion inferior to "int F(void)".
I'm aware that you think "int F()" has some advantages in some
circumstances.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| Date | 2026-03-27 21:27 +0000 |
| Message-ID | <10q6snp$2nka$5@dont-email.me> |
| In reply to | #397221 |
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote: > Using 'static' to force an actual function to be generated sounds > unintuitive. Lots of things about C are “unintuitive”. Coming from Pascal, I found its type-definition syntax completely backwards.
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-27 22:05 +0000 |
| Message-ID | <10q6uv4$419b$1@dont-email.me> |
| In reply to | #397234 |
On 27/03/2026 21:27, Lawrence D’Oliveiro wrote: > On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote: > >> Using 'static' to force an actual function to be generated sounds >> unintuitive. > > Lots of things about C are “unintuitive”. Coming from Pascal, I found > its type-definition syntax completely backwards. If only it was simply backwards! Instead it's inside-out and spirular. But, yeah, some C aspect being unintuitive is nothing new: extern int abc; int abc = 123; Or: static int abc; extern int abc; Both are valid C, but this isn't: extern int abc; static int abc; I dare not ask what the rules are; I'm sure they exist, but they still don't make sense. I can only find out which of these are valid or not by trial and error. A HLL shouldn't be like that.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-03-27 17:03 -0700 |
| Message-ID | <87fr5kx1jr.fsf@example.invalid> |
| In reply to | #397235 |
Bart <bc@freeuk.com> writes:
[...]
> I dare not ask what the rules are; I'm sure they exist, but they still
> don't make sense. I can only find out which of these are valid or not
> by trial and error. A HLL shouldn't be like that.
[...]
Fortunately, there's a standard.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Janis Papanagnou <janis_papanagnou+ng@hotmail.com> |
|---|---|
| Date | 2026-03-28 05:10 +0100 |
| Message-ID | <10q7kb0$aibc$1@dont-email.me> |
| In reply to | #397235 |
On 2026-03-27 23:05, Bart wrote: > On 27/03/2026 21:27, Lawrence D’Oliveiro wrote: >> On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote: >> >>> Using 'static' to force an actual function to be generated sounds >>> unintuitive. >> >> Lots of things about C are “unintuitive”. Coming from Pascal, I found >> its type-definition syntax completely backwards. Is there any [non-C-derived] language that had a similar convoluted form? > If only it was simply backwards! Instead it's inside-out and spirular. LOL! - nice formulation. (You made my day.) (The "C" declaration syntax somehow reminds me the US date format; yeah, neither forwards nor backwards.) Janis
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2026-03-28 20:37 +0300 |
| Message-ID | <20260328203718.00005c70@yahoo.com> |
| In reply to | #397235 |
On Fri, 27 Mar 2026 22:05:24 +0000 Bart <bc@freeuk.com> wrote: > On 27/03/2026 21:27, Lawrence D’Oliveiro wrote: > > On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote: > > > >> Using 'static' to force an actual function to be generated sounds > >> unintuitive. > > > > Lots of things about C are “unintuitive”. Coming from Pascal, I > > found its type-definition syntax completely backwards. > > If only it was simply backwards! Instead it's inside-out and spirular. > > But, yeah, some C aspect being unintuitive is nothing new: > > extern int abc; > int abc = 123; > > Or: > > static int abc; > extern int abc; > > Both are valid C, but this isn't: > > extern int abc; > static int abc; > > > I dare not ask what the rules are; I'm sure they exist, but they > still don't make sense. I can only find out which of these are valid > or not by trial and error. A HLL shouldn't be like that. Why do you care? As C programmer, all you have to know is to never use either 2nd or 3rd, because even if one of them can be legal according to Standard, both of them certainly make no sense. For your first example, it sometimes make sense, typically as result of #inclyde and/or marcro substitution. What exactly do you find unintuitive about it?
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-28 18:33 +0000 |
| Message-ID | <10q96uf$rjbn$1@dont-email.me> |
| In reply to | #397255 |
On 28/03/2026 17:37, Michael S wrote: > On Fri, 27 Mar 2026 22:05:24 +0000 > Bart <bc@freeuk.com> wrote: > >> On 27/03/2026 21:27, Lawrence D’Oliveiro wrote: >>> On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote: >>> >>>> Using 'static' to force an actual function to be generated sounds >>>> unintuitive. >>> >>> Lots of things about C are “unintuitive”. Coming from Pascal, I >>> found its type-definition syntax completely backwards. >> >> If only it was simply backwards! Instead it's inside-out and spirular. >> >> But, yeah, some C aspect being unintuitive is nothing new: >> >> extern int abc; >> int abc = 123; >> >> Or: >> >> static int abc; >> extern int abc; >> >> Both are valid C, but this isn't: >> >> extern int abc; >> static int abc; >> >> >> I dare not ask what the rules are; I'm sure they exist, but they >> still don't make sense. I can only find out which of these are valid >> or not by trial and error. A HLL shouldn't be like that. > > Why do you care? > As C programmer, I'm also an implementer. > all you have to know is to never use either 2nd or > 3rd, because even if one of them can be legal according to Standard, > both of them certainly make no sense. > > For your first example, it sometimes make sense, typically as result of > #inclyde and/or marcro substitution. What exactly do you find > unintuitive about it? That you declare 'abc' as something to be imported, yet in the next line it is initialised as though it was exported.
[toc] | [prev] | [next] | [standalone]
| From | antispam@fricas.org (Waldek Hebisch) |
|---|---|
| Date | 2026-03-29 00:53 +0000 |
| Message-ID | <10q9t6d$2p36t$1@paganini.bofh.team> |
| In reply to | #397256 |
Bart <bc@freeuk.com> wrote:
> On 28/03/2026 17:37, Michael S wrote:
>> On Fri, 27 Mar 2026 22:05:24 +0000
>> Bart <bc@freeuk.com> wrote:
>>
>>> On 27/03/2026 21:27, Lawrence D’Oliveiro wrote:
>>>> On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
>>>>
>>>>> Using 'static' to force an actual function to be generated sounds
>>>>> unintuitive.
>>>>
>>>> Lots of things about C are “unintuitive”. Coming from Pascal, I
>>>> found its type-definition syntax completely backwards.
>>>
>>> If only it was simply backwards! Instead it's inside-out and spirular.
>>>
>>> But, yeah, some C aspect being unintuitive is nothing new:
>>>
>>> extern int abc;
>>> int abc = 123;
>>>
>>> Or:
>>>
>>> static int abc;
>>> extern int abc;
>>>
>>> Both are valid C, but this isn't:
>>>
>>> extern int abc;
>>> static int abc;
>>>
>>>
>>> I dare not ask what the rules are; I'm sure they exist, but they
>>> still don't make sense. I can only find out which of these are valid
>>> or not by trial and error. A HLL shouldn't be like that.
>>
>> Why do you care?
>> As C programmer,
>
> I'm also an implementer.
>
>> all you have to know is to never use either 2nd or
>> 3rd, because even if one of them can be legal according to Standard,
>> both of them certainly make no sense.
>>
>> For your first example, it sometimes make sense, typically as result of
>> #inclyde and/or marcro substitution. What exactly do you find
>> unintuitive about it?
>
> That you declare 'abc' as something to be imported, yet in the next line
> it is initialised as though it was exported.
Well, C is not able to directy express notion of export and import.
Also, C does not have modules/interfaces etc. Instead C object
may be global (which is specified by 'extern' or by lack of storage
class at file scope) or local. C rules allow you to emulate
modules using header files. Namely, in header file you put
'extern' declaration for exports of given module. Any user of
of the module must include the corresponding header file.
To ensure consistency also implementation of the module should
include the header file. So everywhere elso you have just
'extern' declaration, but in implementation there is 'extern'
declaration first followed by the definition. If such
sequence was disallowed, then it would be harder to check
consistency of implementation and corresponding header.
So in reasonable code 'extern' really means "part of interface"
without specifying if it is import or export.
Note that implementers of C decided to use system linker.
When C was born standard linkers did not have support for
modules and due to limits on name length emulating such
support was problematic. So what C did was best thing
possible. We can now do better, but requirement of
compatibility means that old approach is still in use.
To put it differently: any language which decides to
break compatibility (and a lot did so) can do better than
C. But due to compatibility C is more popular than
competitors.
--
Waldek Hebisch
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-29 22:37 +0100 |
| Message-ID | <10qc632$1uds9$1@dont-email.me> |
| In reply to | #397260 |
On 29/03/2026 00:53, Waldek Hebisch wrote:
> Bart <bc@freeuk.com> wrote:
>> That you declare 'abc' as something to be imported, yet in the next line
>> it is initialised as though it was exported.
>
> Well, C is not able to directy express notion of export and import.
> Also, C does not have modules/interfaces etc. Instead C object
> may be global (which is specified by 'extern' or by lack of storage
> class at file scope) or local. C rules allow you to emulate
> modules using header files. Namely, in header file you put
> 'extern' declaration for exports of given module. Any user of
> of the module must include the corresponding header file.
> To ensure consistency also implementation of the module should
> include the header file. So everywhere elso you have just
> 'extern' declaration, but in implementation there is 'extern'
> declaration first followed by the definition. If such
> sequence was disallowed, then it would be harder to check
> consistency of implementation and corresponding header.
>
> So in reasonable code 'extern' really means "part of interface"
> without specifying if it is import or export.
But, the rules are lax, and implementations exploit that by all behaving
a little differently.
TBF, the diverse implementations probably came first, and the standard
had to accommodate existing practice.
It still means it is lax: you can often get away without using 'extern'
for example. Or you can define 'int abc' in several modules but the
linker creates only one definition. (It's stricter now, but tcc for
example still allows that.)
Still, there aren't really many combinations, and no need to have
anything more than one definition, and one forward declaration. The
rules could therefore be simplified, and a compiler could reject
anything unusual unless specifically enabled.
Here's how it works in my ASM synax:
F: # local symbol (a function or variable definition)
G:: # exported symbol
call H* # imported symbol (all instances need the *)
It can be that simple!
(However the assembler allows forward references that C lacks. And there
is no concept of a shared header; a second module that exports H needs
to use it locally as H, while calling G needs G*.)
> Note that implementers of C decided to use system linker.
> When C was born standard linkers did not have support for
> modules and due to limits on name length emulating such
> support was problematic.
This is more about namespaces, a separate topic. You can have exported
and imported symbols without worrying about namespaces, but names may clash.
> So what C did was best thing
> possible. We can now do better, but requirement of
> compatibility means that old approach is still in use.
> To put it differently: any language which decides to
> break compatibility (and a lot did so) can do better than
> C. But due to compatibility C is more popular than
> competitors.
Suppose you want to create a DLL or .so file which exports function 'F'
from this program:
void F() {}
void G() {}
void H() {}
C appears to give no guidance here; the behaviour depends on the compiler:
gcc: exports F, G, H
tcc: exports none
(With some codebases I've seen which don't bother with 'static',
hundreds of symbols could be exported.)
This can be changed using an attribute:
__declspec(dllexport) void F() {}
void G() {}
void H() {}
Now:
gcc: exports F only (G, H are no longer exported)
tcc: exports F
Again, very messy. And ugly.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-03-30 05:33 -0700 |
| Message-ID | <86mrzp1oql.fsf@linuxsc.com> |
| In reply to | #397283 |
Bart <bc@freeuk.com> writes: > On 29/03/2026 00:53, Waldek Hebisch wrote: > >> Bart <bc@freeuk.com> wrote: >> >>> That you declare 'abc' as something to be imported, yet in the next line >>> it is initialised as though it was exported. >> >> Well, C is not able to directy express notion of export and import. >> Also, C does not have modules/interfaces etc. Instead C object >> may be global (which is specified by 'extern' or by lack of storage >> class at file scope) or local. C rules allow you to emulate >> modules using header files. Namely, in header file you put >> 'extern' declaration for exports of given module. Any user of >> of the module must include the corresponding header file. >> To ensure consistency also implementation of the module should >> include the header file. So everywhere elso you have just >> 'extern' declaration, but in implementation there is 'extern' >> declaration first followed by the definition. If such >> sequence was disallowed, then it would be harder to check >> consistency of implementation and corresponding header. >> >> So in reasonable code 'extern' really means "part of interface" >> without specifying if it is import or export. > > But, the rules are lax, and implementations exploit that by all > behaving a little differently. The rules are not lax; they are carefully and precisely defined. To see that one needs to know what the rules are, which in turn depends on reading and understanding the C standard. > TBF, the diverse implementations probably came first, and the > standard had to accommodate existing practice. Diverse implementations certainly came first, including especially the original description of C in "The C Programming Language", by Kernighan and Ritchie. The rules for global symbols in K&R C are markedly different than those in the current C standard. There is a long explanation of different options considered for standard C, given in the C Rationale document.
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-30 14:42 +0100 |
| Message-ID | <10qdujq$2g7uj$1@dont-email.me> |
| In reply to | #397303 |
On 30/03/2026 13:33, Tim Rentsch wrote:> Bart <bc@freeuk.com> writes:
>
>> On 29/03/2026 00:53, Waldek Hebisch wrote:
>>
>>> Bart <bc@freeuk.com> wrote:
>>>
>>>> That you declare 'abc' as something to be imported, yet in the
next line
>>>> it is initialised as though it was exported.
>>>
>>> Well, C is not able to directy express notion of export and import.
>>> Also, C does not have modules/interfaces etc. Instead C object
>>> may be global (which is specified by 'extern' or by lack of storage
>>> class at file scope) or local. C rules allow you to emulate
>>> modules using header files. Namely, in header file you put
>>> 'extern' declaration for exports of given module. Any user of
>>> of the module must include the corresponding header file.
>>> To ensure consistency also implementation of the module should
>>> include the header file. So everywhere elso you have just
>>> 'extern' declaration, but in implementation there is 'extern'
>>> declaration first followed by the definition. If such
>>> sequence was disallowed, then it would be harder to check
>>> consistency of implementation and corresponding header.
>>>
>>> So in reasonable code 'extern' really means "part of interface"
>>> without specifying if it is import or export.
>>
>> But, the rules are lax, and implementations exploit that by all
>> behaving a little differently.
>
> The rules are not lax; they are carefully and precisely defined.
> To see that one needs to know what the rules are, which in turn
> depends on reading and understanding the C standard.
The rules may well be precise, but they can also allow for laxity in how
code is written, and how strictly they are enforced by a compiler.
That is why exactly the same program can pass with 0 warnings or errors,
pass with some warnings, or fail with errors, depending on the options
provided. So:
c:\cx>gcc mcc.c
c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
The first invocation compiled fine. The second generated thousands of
warnings, for the same language standard. With -Werror, it would also fail.
Does the standard also say anything about linking? These two modules:
a.c: int abc; int main(){}
b.c: int abc;
both individually compile with no problems. They also link successfully
using TCC and DMC /C/ compilers.
But gcc (invoking its linker) fails saying there are multiple
definitions of 'abc'.
The point is that a.c + b.c can either form a valid program or not due
to laxness in the language.
Here's another example which I believe you brought up:
static void F();
void F() {}
That first 'static' is what determines F's linkage. However someone
perusing the source code will see 'void F(){...}` and assume it is exported.
But you can also have:
static void F(int a);
static void F(int b);
static void F(int c);
extern void F(int d) {}
static void F(int e);
void F(int f);
static void F(int g);
This is what I call being lax, while still within the rules, whatever
they are.
(In my language, F must be defined in exactly one place. No other
declarations are allowed or needed within the program itself. So such
multiple instances don't arise.
It is effectively stricter than C but enforced through better design.
C's design could also be tightened up, but people don't seem to like
losing the 'flexibility' shown above.)
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2026-03-30 16:53 +0300 |
| Message-ID | <20260330165336.00006a88@yahoo.com> |
| In reply to | #397304 |
On Mon, 30 Mar 2026 14:42:18 +0100 Bart <bc@freeuk.com> wrote: > > (In my language, F must be defined in exactly one place. No other > declarations are allowed or needed within the program itself. So such > multiple instances don't arise. > Do you make distinction between declarations and definitions?
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2026-03-30 18:11 +0100 |
| Message-ID | <10qeasl$2l5ps$1@dont-email.me> |
| In reply to | #397305 |
On 30/03/2026 14:53, Michael S wrote:
> On Mon, 30 Mar 2026 14:42:18 +0100
> Bart <bc@freeuk.com> wrote:
>
>>
>> (In my language, F must be defined in exactly one place. No other
>> declarations are allowed or needed within the program itself. So such
>> multiple instances don't arise.
>>
>
> Do you make distinction between declarations and definitions?
>
Yes. Everything within a program will have a definition. No separate
declarations are needed thanks to the module scheme.
The things that can be defined and shared between modules include
functions, variables, named constants, enumerations, types, structs,
macros. Definitions can also be in any order.
Declarations exist, but they are only for things imported from outside
the program, which will be precompiled external libraries.
This is different from C, where sharing any entity between the modules
of a program requires each module to see its declaration, while the
definition resides in some 'home' module.
That can present difficulties, for example:
char data[]; # in shared header
char data[] = {10,20,30,40,50}; # definition
You want to do sizeof(data) from another module, but it doesn't work
because the size is only known when compiling the home module. Full example:
a.h:
extern char data[]; // extern is apparently needed
a.c:
#include <stdio.h>
#include "a.h"
char data[] = {10, 20, 30, 40, 50};
int F(); // extern is not needed ...
int main() {printf("%zu\n", F());}
b.c:
#include "a.h"
int F() {return sizeof(data);}
Compile using 'cc a.c b.c'. It will complain about sizeof on complete type.
If I try the same thing:
a.m:
module b
global []byte data = (10, 20, 30, 40, 50)
proc main = println F() end
b.m:
global fun F:int = data.len # 'fun' is for one-liners
I compile using 'mm a' and it Just Works. I don't even need to tell it
both module names, or the extension for 'a'. Output is '5'.
Notice here there is one definition for both 'data' and 'F', and no
declaration. (That 'module b' directive is outside the main language.)
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-03-30 08:27 -0700 |
| Message-ID | <865x6d1gnq.fsf@linuxsc.com> |
| In reply to | #397304 |
Bart <bc@freeuk.com> writes:
> On 30/03/2026 13:33, Tim Rentsch wrote:> Bart <bc@freeuk.com> writes:
>
>>> On 29/03/2026 00:53, Waldek Hebisch wrote:
>>>
>>>> Bart <bc@freeuk.com> wrote:
>>>>
>>>>> That you declare 'abc' as something to be imported, yet in the next line
>>>>> it is initialised as though it was exported.
>>>>
>>>> Well, C is not able to directy express notion of export and import.
>>>> Also, C does not have modules/interfaces etc. Instead C object
>>>> may be global (which is specified by 'extern' or by lack of storage
>>>> class at file scope) or local. C rules allow you to emulate
>>>> modules using header files. Namely, in header file you put
>>>> 'extern' declaration for exports of given module. Any user of
>>>> of the module must include the corresponding header file.
>>>> To ensure consistency also implementation of the module should
>>>> include the header file. So everywhere elso you have just
>>>> 'extern' declaration, but in implementation there is 'extern'
>>>> declaration first followed by the definition. If such
>>>> sequence was disallowed, then it would be harder to check
>>>> consistency of implementation and corresponding header.
>>>>
>>>> So in reasonable code 'extern' really means "part of interface"
>>>> without specifying if it is import or export.
>>>
>>> But, the rules are lax, and implementations exploit that by all
>>> behaving a little differently.
>>
>> The rules are not lax; they are carefully and precisely defined.
>> To see that one needs to know what the rules are, which in turn
>> depends on reading and understanding the C standard.
>
> The rules may well be precise, but they can also allow for laxity in
> how code is written, and how strictly they are enforced by a compiler.
>
> That is why exactly the same program can pass with 0 warnings or
> errors, pass with some warnings, or fail with errors, depending on the
> options provided. So:
>
> c:\cx>gcc mcc.c
>
> c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
>
> The first invocation compiled fine. The second generated thousands of
> warnings, for the same language standard. With -Werror, it would also
> fail.
>
> Does the standard also say anything about linking? These two modules:
>
> a.c: int abc; int main(){}
> b.c: int abc;
>
> both individually compile with no problems. They also link
> successfully using TCC and DMC /C/ compilers.
>
> But gcc (invoking its linker) fails saying there are multiple
> definitions of 'abc'.
>
> The point is that a.c + b.c can either form a valid program or not due
> to laxness in the language.
>
> Here's another example which I believe you brought up:
>
> static void F();
> void F() {}
>
> That first 'static' is what determines F's linkage. However someone
> perusing the source code will see 'void F(){...}` and assume it is
> exported.
>
> But you can also have:
>
> static void F(int a);
> static void F(int b);
> static void F(int c);
> extern void F(int d) {}
> static void F(int e);
> void F(int f);
> static void F(int g);
>
> This is what I call being lax, while still within the rules, whatever
> they are.
>
> (In my language, F must be defined in exactly one place. No other
> declarations are allowed or needed within the program itself. So such
> multiple instances don't arise.
>
> It is effectively stricter than C but enforced through better
> design. C's design could also be tightened up, but people don't seem
> to like losing the 'flexibility' shown above.)
It seems to me that your real complaint is not that you don't
understand the rules but that you don't like them. I'm not able
to help you with that.
[toc] | [prev] | [next] | [standalone]
Page 1 of 4 [1] 2 3 4 Next page →
Back to top | Article view | comp.lang.c
csiph-web