News for hackers. Stuff that matters.
Re:Modern C, Second Edition by abainbridge on Wednesday September 18, 2019 @02:15PM (#21008285)
Looks reasonable to me. I've been programming C daily for 25 years. Cosmetic but it is nice to see "int const foo;" being preferred to "const int foo". And "We define variables as close to their first use as possible". Less cosmetically, section 5.6 on named constants is great. That's a fiddly area of C from which to pull out reliable advice.
Re:Modern C, Second Edition by red75prime on Wednesday September 18, 2019 @05:22PM (#21010366)
Still only signed int for typed constants. Are there some plans to mitigate it? It's a pain point for FFI.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @02:22PM (#21008361)
There is no technical reason to prefer one or the other; they are both declaration specifiers which can occur in any order. long const int is valid, even.

The const qualification is in the type; it is not a direct attribute of foo, so putting the const closer to foo means nothing.

Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @02:50PM (#21008642)
The parent says it is "cosmetic", not technical. The parent, being someone who wrote C daily for 25 years, knows what the syntax allows and that it isn't necessary. A constant pointer to a constant pointer to a constant integer should be written "int const * const * const x" because there's only one other way, "const int * const * const x," which is inconsistent, ugly and potentially confusing.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @03:25PM (#21009020)
It's bad cosmetic. The predominant style is "const int", following the English adjective before noun.
Re:Modern C, Second Edition by kccqzy on Wednesday September 18, 2019 @04:13PM (#21009593)
It depends on the perspective. To me and in the codebases I've worked in, the predominant style is "int const" instead of the other way around.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @09:59PM (#21012506)
My perspective is that "const type" order consistently appears in all versions of the ISO C standard and POSIX.

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.
Re:Modern C, Second Edition by braindeath on Wednesday September 18, 2019 @04:14PM (#21009607)
C declarators cannot in general and do not follow English grammar rules, so that is a non-sequitur.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @04:46PM (#21009992)
const int isn't a declarator; it's a list of declaration specifiers.

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.

Re:Modern C, Second Edition by braindeath on Wednesday September 18, 2019 @06:24PM (#21011011)
I think you're inadvertently just proving the point.

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.

Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @09:03PM (#21012198)
Indeed, declarators must have their postfix chain read first, left to right, then the prefix portions right to left. (Separately at each level of parenthesization, if any, working inside out.)

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.
Re:Modern C, Second Edition by braindeath on Wednesday September 18, 2019 @09:48PM (#21012450)
Wait what??

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.

Re:Modern C, Second Edition by kazinator on Thursday September 19, 2019 @02:27AM (#21013820)
We're then missing that the const qualification is applying to the int type.

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.

Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @06:24PM (#21011016)
They literally cannot follow English, so I guess they weren't intended to. The order isn't arbitrary for all elements of the list, only the left-most.
Re:Modern C, Second Edition by dooglius on Wednesday September 18, 2019 @05:11PM (#21010264)
I generally think of "int" as also an adjective, with both "const" and "int" describing the variable.
Re:Modern C, Second Edition by iamnotacrook on Thursday September 19, 2019 @07:39AM (#21015259)
"I generally think of "int" as also an adjective, with both "const" and "int" describing the variable."

Eh? int is a noun, surely? How does int describe an int?

I can imagine cost and long being adjectives for the noun int.

Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @05:48PM (#21010608)
That's fair, and double is obviously an adjective. struct foo maybe not so much.
Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @04:14PM (#21009603)
Your response is an instance of the naturalistic fallacy.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @04:40PM (#21009922)
Sure, except for the part where C is a language that uses English or English-based keywords, designed by native English speakers.
Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @06:17PM (#21010933)
There's no "except" because one cannot conclude how people should write programs from how they do write programs, regardless of circumstances. In any case, languages and their use change. C is 47 years old. Consistent expression structures is more important than analogies to English.

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.

Re:Modern C, Second Edition by CawCawCaw on Saturday September 21, 2019 @05:29AM (#21034346)
So ... anything goes, then?
Re:Modern C, Second Edition by bauerd on Wednesday September 18, 2019 @04:52PM (#21010057)
It's not about moral goodness, it's about developer ergonomics. I don't see why the fallacy would apply to all value judgments
Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @06:23PM (#21011002)
It's isomorphic, but you're right, I used an overly specific term. In any case, one can't conclude that something should be the way it is from the fact that it is the way it is without fruther premises, regardless of the kind of value judgements involved.
Re:Modern C, Second Edition by bauerd on Thursday September 19, 2019 @10:08AM (#21016295)
Implicit key premise seems to be that the more akin a formal (programming) language to it's users' natural one, the more ergonomic it is. I would not necessarily agree, but not read it as ought-from-is.
Re:Modern C, Second Edition by wahern on Wednesday September 18, 2019 @02:48PM (#21008626)
It's a good habit to get into, though, when it comes to pointer types. How many times have people written code like:

  const int *foo;
when they meant

  int *const foo;
or

  const int *const foo;
Re:Modern C, Second Edition by loeg on Wednesday September 18, 2019 @03:22PM (#21008973)
Rarely does someone mean:

    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;
Re:Modern C, Second Edition by JoachimSchipper on Thursday September 19, 2019 @08:44AM (#21015614)
FWIW, I find that form quite useful for defining short aliases, e.g.

    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.)

Re:Modern C, Second Edition by loeg on Friday September 20, 2019 @12:43AM (#21023387)
Yeah, this only works if you initialize at the same time as declaration. And as you say, without const, the same code is generated. All it prevents is accidental reuse of current_foo, which may have some value.
Re:Modern C, Second Edition by tempodox on Thursday September 19, 2019 @03:37AM (#21014145)
> It's mostly useless to declare a variable that cannot be assigned.

So all functional languages (where immutability is the default) are mostly useless?

Re:Modern C, Second Edition by loeg on Friday September 20, 2019 @12:44AM (#21023388)
If you declare a variable in this way in C, it can never be assigned. Not once — zero times.

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.

Re:Modern C, Second Edition by munchbunny on Wednesday September 18, 2019 @04:39PM (#21009909)
When I see "const int *foo" that's when I open my browser and google "C const pointer syntax".

The fiddlyness of const type declarations is definitely one of my least favorite parts of const semantics in C and C++.

Re:Modern C, Second Edition by kevindqc on Wednesday September 18, 2019 @06:02PM (#21010739)
I think you just need to read right to left?

"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?

Re:Modern C, Second Edition by munchbunny on Wednesday September 18, 2019 @09:16PM (#21012266)
Thanks for the explanation. I meant it mostly facetiously, but my more serious point is that there are some parts of some languages (and APIs and tools and architectures) where the answer you get from experienced practitioners is "think harder". It's not that I can't grok it, it's that it takes more effort than it should given how much the concept comes up, and the more I wrestle with my tools, the less I potentially get done.

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.

Re:Modern C, Second Edition by cma on Thursday September 19, 2019 @12:46AM (#21013368)
I think it was needed for operator overloading.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @03:43PM (#21009204)
const-qualified pointers are largely useless. If a struct contains such a thing, it can't be dynamically allocated. As a function parameter, it doesn't protect the caller from anything because passing is by value.

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.
Re:Modern C, Second Edition by sersnth on Thursday September 19, 2019 @12:50AM (#21013387)
Your grep result of Linux is because the regex is bad. `\t` does not seem to be available in the grep basic syntax. It is available with Perl Compatible Regular Expressions, but then you can't use `\>`.

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.
Re:Modern C, Second Edition by eMSF on Wednesday September 18, 2019 @02:35PM (#21008497)
The point isn't about putting it closer to the variable name, but consistently to the right of what it applies to.

But yes, it is a matter of opinion.

Re:Modern C, Second Edition by mumblemumble on Wednesday September 18, 2019 @02:44PM (#21008595)
I'm only an infrequent C programmer, but that goal interests me simply because it seems unattainable given other aspects of C's syntax. If qualifiers went to the right of things they qualify, I'd expect variable declarations to look more like:

  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.
Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @02:52PM (#21008665)
See wahern and my response to the grandparent.
Re:Modern C, Second Edition by mumblemumble on Wednesday September 18, 2019 @03:14PM (#21008887)
So, IOW, it's not purely a point of style, it's more about what's a sane way to write things given the requirements of the language's syntax when you're dealing with pointer declarations?

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.

Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @03:21PM (#21008967)
I think it can be both, plus communication: communication to n00bs, to yourself in the future if you're really tired, etc.
Re:Modern C, Second Edition by mumblemumble on Wednesday September 18, 2019 @04:06PM (#21009498)
Though, if the goal is communication, wouldn't it be clearer still to use typedefs to clean up more complex declarations a bit? Or does that end up making things worse?
Re:Modern C, Second Edition by guerrilla on Wednesday September 18, 2019 @04:10PM (#21009539)
People do that if such a type is used a lot but probably not if it's just a one-off, just like when they write a function instead of using the same snippet a lot but don't if it isn't.
Re:Modern C, Second Edition by kazinator on Wednesday September 18, 2019 @03:09PM (#21008838)
To the right of what it applies to? Like French: Moulin Rouge. :)
Re:Modern C, Second Edition by forty on Wednesday September 18, 2019 @05:02PM (#21010162)
In French it can be either left or right depending on the word: un moulin rouge (a red mill) but un vieux moulin (an old mill)