News for hackers. Stuff that matters.
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.