Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #396068 > unrolled thread
| Started by | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| First post | 2026-01-02 07:24 +0000 |
| Last post | 2026-01-02 19:44 +0000 |
| Articles | 20 on this page of 72 — 16 participants |
Back to article view | Back to comp.lang.c
function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 07:24 +0000
Re: function pointer question Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-01-02 09:04 +0000
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 14:42 +0000
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 14:45 +0000
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-02 02:52 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 14:43 +0000
Re: function pointer question highcrew <high.crew3868@fastmail.com> - 2026-01-02 17:21 +0100
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-02 09:37 -0800
Re: function pointer question Ben Bacarisse <ben@bsb.me.uk> - 2026-01-03 03:33 +0000
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-03 07:41 -0800
Re: function pointer question Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-01-03 21:46 +0000
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-04 12:03 +0100
Re: function pointer question Kaz Kylheku <046-301-5902@kylheku.com> - 2026-01-06 20:41 +0000
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-07 07:18 -0800
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-07 21:52 -0800
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-08 09:17 +0100
Re: function pointer question Kaz Kylheku <046-301-5902@kylheku.com> - 2026-01-02 17:48 +0000
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 19:35 +0000
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-02 12:07 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-03 06:06 +0000
Re: function pointer question Kaz Kylheku <046-301-5902@kylheku.com> - 2026-01-02 21:50 +0000
Re: function pointer question Kaz Kylheku <046-301-5902@kylheku.com> - 2026-01-02 21:52 +0000
Re: function pointer question "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-01-02 14:18 -0800
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-03 13:55 +0100
Re: function pointer question "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-01-03 12:04 -0800
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-03 13:01 -0800
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-07 07:35 -0800
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-07 08:17 -0800
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-07 08:23 -0800
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-07 18:44 -0800
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-07 18:27 -0800
Re: function pointer question Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-01-03 22:05 +0000
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-03 16:39 -0800
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-04 12:15 +0100
Re: function pointer question Kaz Kylheku <046-301-5902@kylheku.com> - 2026-01-06 20:33 +0000
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-06 17:01 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-03 06:08 +0000
Re: function pointer question "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-01-05 12:40 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 04:30 +0000
Re: function pointer question "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-01-06 17:05 -0800
Re: function pointer question James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-01-03 17:20 -0500
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-03 16:48 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-05 08:39 +0000
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 12:32 +0000
Re: function pointer question highcrew <high.crew3868@fastmail.com> - 2026-01-06 13:59 +0100
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 13:57 +0000
Re: function pointer question antispam@fricas.org (Waldek Hebisch) - 2026-01-06 14:50 +0000
Re: function pointer question highcrew <high.crew3868@fastmail.com> - 2026-01-06 21:44 +0100
Re: function pointer question scott@slp53.sl.home (Scott Lurndal) - 2026-01-06 22:08 +0000
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-07 05:59 -0800
Re: function pointer question antispam@fricas.org (Waldek Hebisch) - 2026-01-07 09:25 +0000
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-07 11:37 +0100
Re: function pointer question Michael S <already5chosen@yahoo.com> - 2026-01-06 15:47 +0200
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 14:01 +0000
Re: function pointer question David Brown <david.brown@hesbynett.no> - 2026-01-06 15:55 +0100
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 16:44 +0000
Re: function pointer question scott@slp53.sl.home (Scott Lurndal) - 2026-01-06 15:41 +0000
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 16:45 +0000
Re: function pointer question James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-01-06 10:58 -0500
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-06 16:49 +0000
Re: function pointer question James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-01-06 12:09 -0500
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-07 21:18 +0000
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-09 09:14 -0800
Re: function pointer question Andrey Tarasevich <noone@noone.net> - 2026-01-10 19:17 -0800
Re: function pointer question "James Russell Kuyper Jr." <jameskuyper@alumni.caltech.edu> - 2026-01-10 22:39 -0500
Re: function pointer question Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-01-11 11:49 -0800
Re: function pointer question James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-01-05 06:47 -0500
Re: function pointer question James Kuyper <jameskuyper@alumni.caltech.edu> - 2026-01-02 14:03 -0500
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 19:41 +0000
Re: function pointer question bart <bc@freeuk.com> - 2026-01-02 19:18 +0000
Re: function pointer question Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2026-01-02 11:43 -0800
Re: function pointer question Michael Sanders <porkchop@invalid.foo> - 2026-01-02 19:44 +0000
Page 1 of 4 [1] 2 3 4 Next page →
| From | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-02 07:24 +0000 |
| Subject | function pointer question |
| Message-ID | <10j7rs6$7c9e$1@dont-email.me> |
i have: void moo(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) void mastermind(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) to use either i have: void (*render)(char [][64], int, int, const char *) = MOO ? moo : mastermind; my multi-part question: why is void required for the function pointer? A: because both moo() & mastermind return void? B: because every function must have a return type *including function pointers*? C: what about tyedef? -- :wq Mike Sanders
[toc] | [next] | [standalone]
| From | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| Date | 2026-01-02 09:04 +0000 |
| Message-ID | <10j81nf$9083$2@dont-email.me> |
| In reply to | #396068 |
On Fri, 2 Jan 2026 07:24:55 -0000 (UTC), Michael Sanders wrote: > B: because every function must have a return type > *including function pointers*? Yes. The type of the function pointer is of course that of the type of functions it points to. > C: what about ty[p]edef? The same rule applies as with any typedef: put the name to be given to the type in the place where the variable you might be declaring of that type would go.
[toc] | [prev] | [next] | [standalone]
| From | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-02 14:42 +0000 |
| Message-ID | <10j8lgk$fgsk$1@dont-email.me> |
| In reply to | #396069 |
On Fri, 2 Jan 2026 09:04:47 -0000 (UTC), Lawrence D’Oliveiro wrote: > On Fri, 2 Jan 2026 07:24:55 -0000 (UTC), Michael Sanders wrote: > >> B: because every function must have a return type >> *including function pointers*? > > Yes. The type of the function pointer is of course that of the type of > functions it points to. > >> C: what about ty[p]edef? > > The same rule applies as with any typedef: put the name to be given to > the type in the place where the variable you might be declaring of > that type would go. Many thanks Lawrence, slow & steady, I'm gaining steam. -- :wq Mike Sanders
[toc] | [prev] | [next] | [standalone]
| From | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-02 14:45 +0000 |
| Message-ID | <10j8lmv$fhr0$1@dont-email.me> |
| In reply to | #396069 |
On Fri, 2 Jan 2026 09:04:47 -0000 (UTC), Lawrence D’Oliveiro wrote: >> C: what about ty[p]edef? [sic!] -- :wq Mike Sanders
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-01-02 02:52 -0800 |
| Message-ID | <878qeguw0p.fsf@example.invalid> |
| In reply to | #396068 |
Michael Sanders <porkchop@invalid.foo> writes:
> i have:
>
> void moo(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg)
>
> void mastermind(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg)
>
> to use either i have:
>
> void (*render)(char [][64], int, int, const char *) = MOO ? moo : mastermind;
>
> my multi-part question:
>
> why is void required for the function pointer?
Every function has a type. Every function pointer has a type. For a
function pointer to point to a function, it must have type "pointer to
blah", where "blah" is the type of the function.
A function type includes information about the type of the function's
result and the types of its parameters. The type of both foo and
mastermind can be written as:
void(char[]64], int, int, const char*)
> A: because both moo() & mastermind return void?
Yes.
> B: because every function must have a return type
> *including function pointers*?
Functions and function pointers are of course distinct. A function is
of function type. A function pointer is of an object type, specifically
a pointer-to-function type.
Every pointer-to-function type is derived from a function type, and that
function type specifies the type returned by the function -- "void" in
this case.
> C: what about tyedef?
What about it?
You can define a typedef for the function type, which could make the
code a bit simpler. (It's also common to define a typedef for a
pointer-to-function type, but I prefer to typedef the function type
itself.)
An example based on your code :
```
#include <stdio.h>
void moo(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) {
puts("moo");
}
void mastermind(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) {
puts("mastermind");
}
typedef void functype(char [][64], int, int, const char *);
int main(void) {
for (int MOO = 0; MOO < 2; MOO ++) {
functype *render = MOO ? moo : mastermind;
printf("MOO is %s: ", MOO ? "true" : "false");
render(NULL, 0, 0, "");
}
}
```
The output :
```
MOO is false: mastermind
MOO is true: moo
```
(Incidentally, all-caps identifiers are most commonly used as macro
names, with FILE being a notable exception.)
--
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 | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-02 14:43 +0000 |
| Message-ID | <10j8liu$fgsk$2@dont-email.me> |
| In reply to | #396071 |
On Fri, 02 Jan 2026 02:52:22 -0800, Keith Thompson wrote:
> Michael Sanders <porkchop@invalid.foo> writes:
>> i have:
>>
>> void moo(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg)
>>
>> void mastermind(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg)
>>
>> to use either i have:
>>
>> void (*render)(char [][64], int, int, const char *) = MOO ? moo : mastermind;
>>
>> my multi-part question:
>>
>> why is void required for the function pointer?
>
> Every function has a type. Every function pointer has a type. For a
> function pointer to point to a function, it must have type "pointer to
> blah", where "blah" is the type of the function.
>
> A function type includes information about the type of the function's
> result and the types of its parameters. The type of both foo and
> mastermind can be written as:
>
> void(char[]64], int, int, const char*)
>
>> A: because both moo() & mastermind return void?
>
> Yes.
>
>> B: because every function must have a return type
>> *including function pointers*?
>
> Functions and function pointers are of course distinct. A function is
> of function type. A function pointer is of an object type, specifically
> a pointer-to-function type.
>
> Every pointer-to-function type is derived from a function type, and that
> function type specifies the type returned by the function -- "void" in
> this case.
>
>> C: what about tyedef?
>
> What about it?
>
> You can define a typedef for the function type, which could make the
> code a bit simpler. (It's also common to define a typedef for a
> pointer-to-function type, but I prefer to typedef the function type
> itself.)
>
> An example based on your code :
>
> ```
> #include <stdio.h>
>
> void moo(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) {
> puts("moo");
> }
>
> void mastermind(char HISTORY[][64], int hst_len, int invalid, const char *gme_msg) {
> puts("mastermind");
> }
>
> typedef void functype(char [][64], int, int, const char *);
>
> int main(void) {
> for (int MOO = 0; MOO < 2; MOO ++) {
> functype *render = MOO ? moo : mastermind;
> printf("MOO is %s: ", MOO ? "true" : "false");
> render(NULL, 0, 0, "");
> }
> }
> ```
>
> The output :
>
> ```
> MOO is false: mastermind
> MOO is true: moo
> ```
>
> (Incidentally, all-caps identifiers are most commonly used as macro
> names, with FILE being a notable exception.)
Excellent reply, appreciate it (especially examples).
--
:wq
Mike Sanders
[toc] | [prev] | [next] | [standalone]
| From | highcrew <high.crew3868@fastmail.com> |
|---|---|
| Date | 2026-01-02 17:21 +0100 |
| Message-ID | <10j8r9n$ha1t$1@dont-email.me> |
| In reply to | #396075 |
On 1/2/26 3:43 PM, Michael Sanders wrote: > (It's also common to define a typedef for a > pointer-to-function type, but I prefer to typedef the function type > itself OK, that's crazy! I didn't know of this! It seems a bit weird at first, but I like the type consistency: you declare `typedef int callback(int)` and declare `callback *c` making it very explicit that we are talking of a pointer. On the other hand it is not clear what `callback c` should do, but it makes sense as soon as you declare it. Thanks for sharing! -- High Crew
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-01-02 09:37 -0800 |
| Message-ID | <874ip3vrua.fsf@example.invalid> |
| In reply to | #396077 |
highcrew <high.crew3868@fastmail.com> writes:
> On 1/2/26 3:43 PM, Michael Sanders wrote:
>> (It's also common to define a typedef for a
>> pointer-to-function type, but I prefer to typedef the function type
>> itself
>
> OK, that's crazy! I didn't know of this!
I wrote the above ("It's also common..."), not Michael.
> Thanks for sharing!
You're welcome.
--
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 | Ben Bacarisse <ben@bsb.me.uk> |
|---|---|
| Date | 2026-01-03 03:33 +0000 |
| Message-ID | <87ms2v8j4t.fsf@bsb.me.uk> |
| In reply to | #396077 |
highcrew <high.crew3868@fastmail.com> writes:
> On 1/2/26 3:43 PM, Michael Sanders wrote:
>> (It's also common to define a typedef for a
>> pointer-to-function type, but I prefer to typedef the function type
>> itself
>
> OK, that's crazy! I didn't know of this!
>
> It seems a bit weird at first, but I like the type consistency:
> you declare `typedef int callback(int)` and declare `callback *c`
> making it very explicit that we are talking of a pointer.
>
> On the other hand it is not clear what `callback c` should do,
> but it makes sense as soon as you declare it.
Declarations using the function type do often make sense. Note
"declarations" -- you can't use the typedef'd name to /define/ a function
of the given type.
A pattern I've used more than once when setting up a table of function
pointers that act like op-codes. Maybe you have an add function, a sub
function, a mul functions and a div function. These are all defined in
a file arithmetic.c, but the table (maybe in another file) needs to see
declarations of the names:
typedef double operation(double, double);
/* ... */
extern operation add, sub, mul, div;
static struct {
char *name;
operation *function;
} ops[] = {
{ "add", add },
{ "subtract", sub },
{ "multiply", mul },
{ "divide", div }
};
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-01-03 07:41 -0800 |
| Message-ID | <10jbdbd$1b3h1$1@dont-email.me> |
| In reply to | #396096 |
On Fri 1/2/2026 7:33 PM, Ben Bacarisse wrote:
>
> A pattern I've used more than once when setting up a table of function
> pointers that act like op-codes. Maybe you have an add function, a sub
> function, a mul functions and a div function. These are all defined in
> a file arithmetic.c, but the table (maybe in another file) needs to see
> declarations of the names:
>
> typedef double operation(double, double);
> /* ... */
>
> extern operation add, sub, mul, div;
>
> static struct {
> char *name;
> operation *function;
> } ops[] = {
> { "add", add },
> { "subtract", sub },
> { "multiply", mul },
> { "divide", div }
> };
>
... with a remark that `extern` is completely redundant here.
operation add, sub, mul, div;
is already a proper bunch of non-defining function declarations.
`extern` does not make it more "non-defining" than it already is.
--
Best regards,
Andrey.
[toc] | [prev] | [next] | [standalone]
| From | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| Date | 2026-01-03 21:46 +0000 |
| Message-ID | <10jc2mv$1i4ia$3@dont-email.me> |
| In reply to | #396111 |
On Sat, 3 Jan 2026 07:41:33 -0800, Andrey Tarasevich wrote: > ... with a remark that `extern` is completely redundant here. One of those quirks of C, that “extern” means something in some places, but is redundant in others.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2026-01-04 12:03 +0100 |
| Message-ID | <10jdhdo$1ui8e$1@dont-email.me> |
| In reply to | #396120 |
On 03/01/2026 22:46, Lawrence D’Oliveiro wrote: > On Sat, 3 Jan 2026 07:41:33 -0800, Andrey Tarasevich wrote: > >> ... with a remark that `extern` is completely redundant here. > > One of those quirks of C, that “extern” means something in some > places, but is redundant in others. It is redundant here to the compiler, but might still be helpful to human readers.
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <046-301-5902@kylheku.com> |
|---|---|
| Date | 2026-01-06 20:41 +0000 |
| Message-ID | <20260106123503.456@kylheku.com> |
| In reply to | #396111 |
On 2026-01-03, Andrey Tarasevich <noone@noone.net> wrote:
> On Fri 1/2/2026 7:33 PM, Ben Bacarisse wrote:
>>
>> A pattern I've used more than once when setting up a table of function
>> pointers that act like op-codes. Maybe you have an add function, a sub
>> function, a mul functions and a div function. These are all defined in
>> a file arithmetic.c, but the table (maybe in another file) needs to see
>> declarations of the names:
>>
>> typedef double operation(double, double);
>> /* ... */
>>
>> extern operation add, sub, mul, div;
>>
>> static struct {
>> char *name;
>> operation *function;
>> } ops[] = {
>> { "add", add },
>> { "subtract", sub },
>> { "multiply", mul },
>> { "divide", div }
>> };
>>
>
> ... with a remark that `extern` is completely redundant here.
>
> operation add, sub, mul, div;
Butlonly because operation is a function type, so the concept of
a tentative definition doesn't apply.
The lack of extern could trip someone up who refactors the
code such that the operations are object types:
named_operation add, sub, mul, div; // oops, multiple definition
static named_operation ops[] = { add, sub, mul, div };
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazinator@mstdn.ca
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-01-07 07:18 -0800 |
| Message-ID | <10jltg8$nqav$1@dont-email.me> |
| In reply to | #396228 |
On Tue 1/6/2026 12:41 PM, Kaz Kylheku wrote:
>>>
>>> typedef double operation(double, double);
>>> /* ... */
>>>
>>> extern operation add, sub, mul, div;
>>>
>>> static struct {
>>> char *name;
>>> operation *function;
>>> } ops[] = {
>>> { "add", add },
>>> { "subtract", sub },
>>> { "multiply", mul },
>>> { "divide", div }
>>> };
>>>
>>
>> ... with a remark that `extern` is completely redundant here.
>>
>> operation add, sub, mul, div;
>
> Butlonly because operation is a function type, so the concept of
> a tentative definition doesn't apply.
Yes, to me it feels like it has way less potential to mislead with an
`extern`. But I can't really say whether this perception is objectively
inherent in the construct, or it is just the fact that it is an exotic
way of declaring functions (for me and, probably, for most people).
However, while it is true that the concept of a tentative definition
doesn't apply, I still don't quite get your point. What if were an
object type and the concept would apply? Are you implying that tentative
definitions should be avoided (i.e. that all object definitions should
include an initializer)?
> The lack of extern could trip someone up who refactors the
> code such that the operations are object types:
>
> named_operation add, sub, mul, div; // oops, multiple definition
>
> static named_operation ops[] = { add, sub, mul, div };
Again, I'm at a bit of a loss. What is this intended to illustrate?
--
Best regards,
Andrey
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-01-07 21:52 -0800 |
| Message-ID | <86h5swodm7.fsf@linuxsc.com> |
| In reply to | #396264 |
Andrey Tarasevich <noone@noone.net> writes:
> On Tue 1/6/2026 12:41 PM, Kaz Kylheku wrote:
>
>>>> typedef double operation(double, double);
>>>> /* ... */
>>>>
>>>> extern operation add, sub, mul, div;
>>>>
>>>> static struct {
>>>> char *name;
>>>> operation *function;
>>>> } ops[] = {
>>>> { "add", add },
>>>> { "subtract", sub },
>>>> { "multiply", mul },
>>>> { "divide", div }
>>>> };
>>>
>>> ... with a remark that `extern` is completely redundant here.
>>>
>>> operation add, sub, mul, div;
>>
>> Butlonly because operation is a function type, so the concept of
>> a tentative definition doesn't apply.
>
> Yes, to me it feels like it has way less potential to mislead with an
> extern`. But I can't really say whether this perception is objectively
> inherent in the construct, or it is just the fact that it is an exotic
> way of declaring functions (for me and, probably, for most people).
>
> However, while it is true that the concept of a tentative definition
> doesn't apply, I still don't quite get your point. What if were an
> object type and the concept would apply? Are you implying that
> tentative definitions should be avoided (i.e. that all object
> definitions should include an initializer)?
If we ignore "static" for the moment, there is a simple rule:
use 'extern' for declarations, and nothing for definitions.
This rule works for both functions and objects.
Now if we add "static" back into the mix, and limit the discussion
to functions, the same rule applies provided we stipulate that
everything is pre-declared. Thus, always use 'extern' or 'static'
to declare (in advance) a function, and on the function definition
don't use any storage class.
Unfortunately, the way 'static' works for objects is not symmetric.
There is no way to write a non-defining declaration for a static
object. And, what is worse, once an object has been declared (and
so tentatively defined) with 'static', then any definition must also
use 'static'. Hence we have a new rule: always use 'extern' or
'static' when declaring a function or object, and leave off both
when defining a function or object, /except/ 'static' must be used
when defining a static object. C would have been nicer if 'static'
for objects worked the same way as 'static' for functions. Oh well.
>> The lack of extern could trip someone up who refactors the
>> code such that the operations are object types:
>>
>> named_operation add, sub, mul, div; // oops, multiple definition
>>
>> static named_operation ops[] = { add, sub, mul, div };
>
> Again, I'm at a bit of a loss. What is this intended to illustrate?
To me the example looks flawed (and not because of multiple
definitions). My advice is to stick with the rule described above
for declarations and definitions.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2026-01-08 09:17 +0100 |
| Message-ID | <10jnp6u$1ac1b$1@dont-email.me> |
| In reply to | #396294 |
On 08/01/2026 06:52, Tim Rentsch wrote:
> Andrey Tarasevich <noone@noone.net> writes:
>
>> On Tue 1/6/2026 12:41 PM, Kaz Kylheku wrote:
>>
>>>>> typedef double operation(double, double);
>>>>> /* ... */
>>>>>
>>>>> extern operation add, sub, mul, div;
>>>>>
>>>>> static struct {
>>>>> char *name;
>>>>> operation *function;
>>>>> } ops[] = {
>>>>> { "add", add },
>>>>> { "subtract", sub },
>>>>> { "multiply", mul },
>>>>> { "divide", div }
>>>>> };
>>>>
>>>> ... with a remark that `extern` is completely redundant here.
>>>>
>>>> operation add, sub, mul, div;
>>>
>>> Butlonly because operation is a function type, so the concept of
>>> a tentative definition doesn't apply.
>>
>> Yes, to me it feels like it has way less potential to mislead with an
>> extern`. But I can't really say whether this perception is objectively
>> inherent in the construct, or it is just the fact that it is an exotic
>> way of declaring functions (for me and, probably, for most people).
>>
>> However, while it is true that the concept of a tentative definition
>> doesn't apply, I still don't quite get your point. What if were an
>> object type and the concept would apply? Are you implying that
>> tentative definitions should be avoided (i.e. that all object
>> definitions should include an initializer)?
>
> If we ignore "static" for the moment, there is a simple rule:
> use 'extern' for declarations, and nothing for definitions.
> This rule works for both functions and objects.
>
> Now if we add "static" back into the mix, and limit the discussion
> to functions, the same rule applies provided we stipulate that
> everything is pre-declared. Thus, always use 'extern' or 'static'
> to declare (in advance) a function, and on the function definition
> don't use any storage class.
>
> Unfortunately, the way 'static' works for objects is not symmetric.
> There is no way to write a non-defining declaration for a static
> object. And, what is worse, once an object has been declared (and
> so tentatively defined) with 'static', then any definition must also
> use 'static'. Hence we have a new rule: always use 'extern' or
> 'static' when declaring a function or object, and leave off both
> when defining a function or object, /except/ 'static' must be used
> when defining a static object. C would have been nicer if 'static'
> for objects worked the same way as 'static' for functions. Oh well.
>
I use a different rule that is more consistent - at least for the way I
organise my code. For different organisations of code and files, it
might not work as well.
Any function or object that is to be externally linked is declared with
"extern" in an appropriate header (usually "file.h", where the
definition is in "file.c", but there are occasional exceptions). Inside
the C file, functions and objects are defined either with "static" (for
internal linkage) or without a storage class specifier (for external
linkage). The C file always includes the header that declares its
extern functions and objects, so that the compiler can check for
consistency.
If I need to have a forward declaration of a static function (I rarely
do, but that can vary by coding style), the forward declaration is made
"static" and the function definition is also marked "static". While you
could omit the "static" on the definition after a forward declaration, I
think it is good to keep it for consistency and clarity (similar to the
use of "extern" on function declarations in headers).
The result is greater consistency than your scheme, and without the need
for a redundant table of forward declarations for static functions. (I
have never really understood why some people like such lists - they give
no new information, force you to jump around back and forth in the file
when adding or changing functions, can easily get inconsistent, and tell
you nothing that you can't easily see with any half-decent IDE or
programmer's editor).
And the scheme can easily be checked with gcc warning flags:
-Wredundant-decls
-Wmissing-declarations
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <046-301-5902@kylheku.com> |
|---|---|
| Date | 2026-01-02 17:48 +0000 |
| Message-ID | <20260102091518.226@kylheku.com> |
| In reply to | #396068 |
On 2026-01-02, Michael Sanders <porkchop@invalid.foo> wrote:
> B: because every function must have a return type
> *including function pointers*?
What it is you think type is, in the context of C?
Does type survive into run-time?
If a function pointer is missing type information about return type, and
that function pointer is needed for expressing a function call, where
does the compiler get the type from?
> C: what about tyedef?
What do you think /that/ is?
The typedef declaration is a way of introducing a name, which names a
type, and can then be used in a declaration, or in the cast notation.
The name is an alias for that type.
typedef isn't a type defining mechanism; it is a mechanism for defining
alias names for types ("typedef names" or "typedefs"). The types that
are aliased by typedef names are specified in the ordinary declaration
syntax, the same as like when a variable is being declared rather than a
typedef name.
Syntactically, typedef is a "storage class specifier", like "extern"
or "static". Thus "extern typedef int x" would never make sense,
due to storage class specifiers being exclusive.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazinator@mstdn.ca
[toc] | [prev] | [next] | [standalone]
| From | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-02 19:35 +0000 |
| Message-ID | <10j96mn$jrsp$1@dont-email.me> |
| In reply to | #396081 |
On Fri, 2 Jan 2026 17:48:16 -0000 (UTC), Kaz Kylheku wrote: > On 2026-01-02, Michael Sanders <porkchop@invalid.foo> wrote: >> B: because every function must have a return type >> *including function pointers*? > > What it is you think type is, in the context of C? > > Does type survive into run-time? > > If a function pointer is missing type information about return type, and > that function pointer is needed for expressing a function call, where > does the compiler get the type from? Its void that's throwing me Kaz. I'm not sure what to think when it comes to void pointers. A void pointer (the pointer itself) is not of the type it points to no? Its typeless & unknown at compile time A 'normal' pointer in C is a variable that stores the address of another variable. And *is of the type it points to*... char str[] = "learning publicly is a humbling experience."; char *ptr = str; Then void enters stage left... void *genericPointer; It can point to an integer, a character, a structure, or any other type. void *genericPointer = &intValue; void *genericPointer = str[]; And it seems to survive a lack of type at compile time. Man I'm confused on this. I mean I get void is flexible, but 'void' is not void in any sense... its like a mask in some way (I'm groping for a definition) I need to study void more. -- :wq Mike Sanders
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2026-01-02 12:07 -0800 |
| Message-ID | <87v7hju6c9.fsf@example.invalid> |
| In reply to | #396084 |
Michael Sanders <porkchop@invalid.foo> writes:
> On Fri, 2 Jan 2026 17:48:16 -0000 (UTC), Kaz Kylheku wrote:
>> On 2026-01-02, Michael Sanders <porkchop@invalid.foo> wrote:
>>> B: because every function must have a return type
>>> *including function pointers*?
>>
>> What it is you think type is, in the context of C?
>>
>> Does type survive into run-time?
>>
>> If a function pointer is missing type information about return type, and
>> that function pointer is needed for expressing a function call, where
>> does the compiler get the type from?
>
> Its void that's throwing me Kaz. I'm not sure what to think when
> it comes to void pointers.
There were no void pointers in the original code. There were function
pointers to functions that return void.
> A void pointer (the pointer itself) is not of the type
> it points to no? Its typeless & unknown at compile time
No pointer is of the type it points to. `int` and `int*` are distinct
types. (The latter is "derived" from the former.)
> A 'normal' pointer in C is a variable that stores the address
> of another variable.
Yes, if by "pointer" you mean "pointer object". The standard also uses
the word "pointer" for a *value* of pointer type, for example the result
of a call to malloc().
(The word "variable" is tricky, and the standard rarely uses it.
"Object" is less ambiguous.)
> And *is of the type it points to*...
No.
> char str[] = "learning publicly is a humbling experience.";
> char *ptr = str;
And now you're introducing array-to-pointer conversion, a can of
worms that I suggest needn't be opened at this time. A simpler
example:
char c = 'l';
char *ptr = &c;
> Then void enters stage left...
>
> void *genericPointer;
>
> It can point to an integer, a character, a structure, or any other type.
>
> void *genericPointer = &intValue;
"intObject" would be a better name. An object (variable you like)
is not a value; it holds a value.
> void *genericPointer = str[];
That's a syntax error, but it's ok if you drop the "[]".
void* is a distinct type. Unlike (most) other object pointer types, it
can't be dereferenced, because it doesn't specify a directly usable type
for the object it points to.
Given:
int n = 42;
int *ip = &n;
void *vp = &n;
ip points to n. vp, in a sense, does not, but converting vp
from void* to int* yields a pointer value that does point to n.
A void* holds something you can think of as a raw untyped address.
It doesn't point to anything, but converting it to foo* gives you
a pointer to an object of type foo.
> And it seems to survive a lack of type at compile time.
> Man I'm confused on this. I mean I get void is flexible,
> but 'void' is not void in any sense... its like a mask in some
> way (I'm groping for a definition) I need to study void more.
"void" is an incomplete type that cannot be completed.
"void*" is a pointer type with some special characteristics.
First, a value of any object pointer type can be converted to void* and
back again, yielding the original pointer value. This is not guaranteed
for (most) other pointer types. For example, converting a char* value
to int* can lose information. (On most real-world systems, all object
pointers, and often all function pointers as well, have the same
representation, but the standard doesn't guarantee this.)
Second, conversions between void* and other object pointer types are
often performed implicitly. You can have an object pointer on one
side of an assignment and a void* pointer on the other side. On some
(exotic) systems, this conversion might do something more than just
copying the representation.
The character pointer types char*, signed char*, and unsigned char*
share the first characteristic, but not the second. (In very early
(pre-ANSI) C, char* was commonly used as a generic pointer type before
void and void* were introduced.)
Every pointer value, object, or expression has a well defined type,
specifically a pointer type. That type specifies the type of
the object it points to -- unless the pointer is of type void*.
(Or it can point to a function; object pointers and function pointers
are distinct.)
The comp.lang.c FAQ <https://www.c-faq.com/> has some good information,
though some of it is a bit dated. Section 4 covers pointers, and
section 6 covers arrays and pointers.
--
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 | Michael Sanders <porkchop@invalid.foo> |
|---|---|
| Date | 2026-01-03 06:06 +0000 |
| Message-ID | <10jabkh$11n14$1@dont-email.me> |
| In reply to | #396088 |
On Fri, 02 Jan 2026 12:07:02 -0800, Keith Thompson wrote: > The comp.lang.c FAQ <https://www.c-faq.com/> has some good information, > though some of it is a bit dated. Section 4 covers pointers, and > section 6 covers arrays and pointers. Sure enough, plan on reading that Monday. Thanks. -- :wq Mike Sanders
[toc] | [prev] | [next] | [standalone]
Page 1 of 4 [1] 2 3 4 Next page →
Back to top | Article view | comp.lang.c
csiph-web