The const qualification is in the type; it is not a direct attribute of foo, so putting the const closer to foo means nothing.
But yes, it is a matter of opinion.
foo: int const;
And similarly, function declarations might also take a more ML-like syntax. C seems more like it wants to be an adjectives-before-nouns type of language.Which would explain why it seems so odd to me; I spent a fair amount of time in C-style languages, but typically only ones that lack pointers.
const int *foo;
when they meant int *const foo;
or const int *const foo; int *const foo;
It's mostly useless to declare a variable that cannot be assigned. But very often people write const int **foo;
When they intended const int * const *foo;So all functional languages (where immutability is the default) are mostly useless?
What is or is not good practice in C has little bearing on or relationship to functional languages, so I'm not sure why you bring them up in this context.
int *const current_foo = &items[i].bar.foo;
before some complicated manipulations on current_foo.(A decent (SSA-)optimizing compiler will produce identical code with and without the declaration.)
Let's see how many times this occurs in my TXR project (~75K lines):
$ git grep -F '*const'
$ git grep -F '* const'
linenoise/linenoise.c: const wchar_t * const *lelem = convert(const wchar_t * const *, larg);
linenoise/linenoise.c: const wchar_t * const *relem = convert(const wchar_t * const *, rarg);
One place: a comparison function for a qsort call, where a vector of const wchar_t * strings is being sorted. I didn't write this (though I did convert it to wchar). qsort passes const void (star) pointers to the elements, which are const wchar_t (star), thus things end up like this.How about Linux kernel 4.9? There are numerous occurrences of this involving arrays declared at file scope, like this:
static const char *const foo[] = { "abc", "def" };
Let's filter those out to find other uses: # lines that start with at least one tab and contain *const,
# possibly with optional spaces between * and const:
kernel-4.9$ git grep '^[\t].*\*[ ]*const\>'
kernel-4.9$
Wow, not a single result!Let's relax that and allow a space or tab [\t ]. Then there are lots of false positives due to items in block commented lines starting with a ' asterisk' sequence. If we require at least one lower-case alpha character before star-const, we filter most of these out:
kernel-4.9$ git grep '^[\t ].*[a-z].*\*[ ]*const\>'
Documentation only, showing useless const parameters in a function prototype decl: Documentation/cpuidle/driver.txt: const struct cpumask *const coupled_cpus);
Documentation/media/kapi/v4l2-controls.rst: s32 skip_mask, s32 def, const char * const *qmenu);
False positive, in a table inside a block comment: arch/powerpc/xmon/ansidecl.h: PTRCONST `void *const' `char *'
Useless const parameter in function pointer signature. The definition of these operations are not required to use const: drivers/net/wan/lmc/lmc_var.h: void (* set_circuit_type)(lmc_softc_t * const, int);
drivers/net/wan/lmc/lmc_var.h: void (* watchdog)(lmc_softc_t * const);
Real result: drivers/of/fdt.c: const char *const *compat)
Real result: drivers/staging/vc04_services/interface/vchi/vchi.h: const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */
Fluff sibling: drivers/staging/vc04_services/interface/vchi/vchi.h: void * const bulk_handle );
Low-value const parameter in function definition: drivers/usb/mon/mon_text.c: char __user * const buf, const size_t nbytes)
Real result (Yacc/Bison skeleton origin): scripts/genksyms/parse.tab.c_shipped: YYSTYPE const * const yyvaluep;
scripts/genksyms/parse.tab.c_shipped: YYSTYPE const * const yyvaluep;
scripts/kconfig/zconf.tab.c_shipped: YYSTYPE const * const yyvaluep;
scripts/kconfig/zconf.tab.c_shipped: YYSTYPE const * const yyvaluep;
Useless const pointer arg in prototype: tools/lib/subcmd/parse-options.h: const char * const usagestr[], int flags);
That's it; a mere spec of dust relative to the LOC count in this tree, half of it useless.Changing it to
$ git grep -P '^[\t ].*[a-z].*\*[ ]*const\W' v4.9
returns 1,779 matches. Here are some from kernel/ v4.9:kernel/time/posix-cpu-timers.c: struct signal_struct *const sig = tsk->signal;
v4.9:kernel/tracepoint.c: struct tracepoint * const *end)
v4.9:kernel/tracepoint.c: struct tracepoint * const *iter;
v4.9:kernel/tracepoint.c: struct tracepoint * const *end,
v4.9:kernel/tracepoint.c: struct tracepoint * const *iter;
Many of the matches are the same as your "array of string constants" example, but just defined at local scope. And many are just constant local variables which some may not consider worth making const. The ones from tracepoint.c above are probably walking an array of const pointers by pointer instead of by index for example though, which is a common case where pointers to const pointers are used, which are legitimately useful.The fiddlyness of const type declarations is definitely one of my least favorite parts of const semantics in C and C++.
"const int * foo" means that foo is a pointer to an int that is constant.
Which could also be "int const * foo" foo is a pointer to a constant int.
But since the const qualifier for pointers can't be reordered like this, I think the point is that it's a better practice to have the const come after, so that it ALWAYS come after in your codebase regardless of the context?
C++ rvalue references are a great example. Whenever I see them I have to go back and relearn the concept because accounting for the language feature sometimes feels more complicated to get right than the unsafe pointer chucking it replaced.
Every major system API I can remember working with that didn't hide const behind a typedef (like Win32's LPCSTR and whatnot) has it const first.
Speaking of Microsoft, though the API's hide const with a typedef, the MSDN samples are predominantly "const char" order.
I've never seen a man page with "int const".
Searching all of the man pages I have installed on an Ubuntu 18 system here (literally grepping through all of /usr/share/man), I found just one with "char const" or "int const". Namely this one:
$ man guestfs-hacking
This contains: int
commandvf (char **stdoutput, char **stderror, unsigned flags,
char const *const *argv)
In the same man page, there are other declarations that have the predominant convention: int
commandrf (char **stdoutput, char **stderror, unsigned flags,
const char *name, ...)
You just worked on some unusual code bases, and are glossing over the language and system API's those code bases depended on, and their respective docs.Further, C is not like English and even if it were, one could not conclude that it should be or should continue to be. Moreover, that it was designed by English speakers is not evidence that it is like English or should be like English: most assembly languages were also desiged by English speakers.
Though these can be reordered, it's clear they were intended to follow English.
In a compiler implementation, it's troublesome to enforce the order compared to processing a simple list and setting/checking flags in some data structure, diagnosing invalid duplicates or mutually exclusive situations.
I was speaking of declarators, not declaration specifiers. In C, a pointer type is part of the declarator and not the specifiers. Therefore, the CV qualifier is also part of the declarator except for the one exception of the specifier. Regardless of whether specifiers were intended to follow English (and this claim is dubious), declarators certainly were not, since it's often more natural to read them right to left, and it's generally most natural to read the left most type-specifier last.
example: char *x[10]; Translated to english this is naturally stated as an array of 10 pointers to char. Not char something something array.
Edit: later on I see you've moved the goal posts about the "uselessness" of cv qualifiers on pointers. I don't know what to tell you, I think that's bullshit, and there are a number of viable use-cases for them regardless of whether you have encountered them. I'm just addressing the consistency part here.
There is no choice about where to put const inside a declarator; the position affects the nesting level which alters the semantics. So there is no debate to be had there.
Note that in declarators, the const is necessarily to the left of the thing it qualifies, so why would we put it to the right of int:
int ** const ptr;
^^^ this is what is const
int * const *ptr;
^^^^ this is what is const
const int **ptr;
^^^ ^^^^^ these two are const
In the canonical specifier order, nothing to the left of const is tainted by const: extern const int **ptr;
^^^ ^^^^^^ const and const
^^^^^^ unrelated to const
For the best possible consistency, imagine const to be pissing, while the wind is blowing left to right.Your third example could just as easily and correctly be:
int const **ptr;
^^^^^ this is what is const
True, it is a special case when there is more than one item in the declarator list as to how const applied, but that by itself is secondary to where const goes.Const qualifies the object. Whether the const is applying to the "int" in some way or is an independent type specifier that applies to each declarator is pretty philosophical. I say it's the latter. The fact is, the very allowance of a cv qualifier in the specifier list introduces an inconsistency or imbalance.
Ultimately this is one of the ultimate bikeshedding argument. There's no right answer, and appeals to spoken language aren't particularly compelling when bikeshedding C of all languages. This isn't AppleScript.
I'm not even terribly interested in this particular point. I'm more interested in quashing the notions that bikeshedded opinions of language syntax have some divine providence. It's just bullshit.
Also, declarations can have multiple declarators.
int const *p, volatile **q[3]; // syntax error! not how it works
This isn't bike-shedding. The shed has already been painted. There is a right answer which is not to make your code look weird just because the standard allows it.No "int const", no i[array] instead of array[i], no deceptive trompe d'oeil nonsense like:
int* p, q;
You will not solve any issue in C programming by swapping around declarators.When we write code, stuff that looks weird should signal a bug. When bug-free code looks weird, that is a distracting false positive that grabs my attention for no reason. Don't add gratuitous weird.
Eh? int is a noun, surely? How does int describe an int?
I can imagine cost and long being adjectives for the noun int.