Easy rule to read complicated const declarations?

15,119

Solution 1

The const modifier is trivial: it modifies what precedes it, unless nothing precedes it. So:

char const* buffer;  // const modifies char
char* const buffer;  // const modifies *

, etc. Generally, It's best to avoid the forms where nothing precedes the const, but in practice, you're going to see them, so you have to remember that when no type precedes the const, you have to logically move it behind the first type. So:

const char** buffer;

is in fact:

char const** buffer;

, i.e. pointer to pointer to const char.

Finally, in a function declaration, a [] after reads as a * before. (Again, it's probably better to avoid this misleading notation, but you're going to see it, so you have to deal with it.) So:

char * const argv[],  //  As function argument

is:

char *const * argv,

a pointer to a const pointer to a char.

Solution 2

(Trying to focus on other aspects of the question)

The rule of thumb for const declarations is to read them from right to left and const modifies the next token. Exception: At the beginning of a declaration const modifies the previous token.

There is a rationale behind this exception - for elementary declarations const char c looks for some people more natural than char const c - and it is reported that a precursor form of const char c predates the final const rule.

getopt

int getopt(int argc, char * const argv[], const char *optstring);

or

int getopt(int argc, char * const * argv, const char *optstring);

Which means that argv is a pointer to const vector of pointers to non-const strings.

But one would expect following declaration:

int getopt(int argc, char const * const * argv, const char *optstring);

(pointer to const vector to const strings)

Because getopt() is not supposed to change the strings referenced via argv.

At least char ** (as used in main()) automatically converts to char * const * argv.

Clang

ASTUnit::LoadFromCommandLine(...,  const char **argv, ...);

Which means that argv is a pointer to a non-const array of pointers to const strings.

Again one would expect const char * const *argv for the same reason as above.

But this is more noticeable because char ** does not convert to const char **, e.g.

int main(int argc, char **argv) {
  const char **x = argv; // Compile error!
  return 0;
}

yields a compile error, where

int main(int argc, char **argv) {
  char * const *x = argv;
  return 0;
}

and

int main(int argc, char **argv) {
  const char * const *x = argv;
  return 0;
}

do not.

Share:
15,119
maxschlepzig
Author by

maxschlepzig

My name is Georg Sauthoff. 'Max Schlepzig' is just a silly old pseudonym (I am hesitant to change it because existing @-replies will not be updated) I studied computer science In my current line of work, I work on trading system software and thus care about low-latency

Updated on June 08, 2022

Comments

  • maxschlepzig
    maxschlepzig about 2 years

    For reading complex pointer declarations there is the right-left rule.

    But this rule does not mention how to read const modifiers.

    For example in a simple pointer declaration, const can be applied in several ways:

    char *buffer; // non-const pointer to non-const memory
    const char *buffer; // non-const pointer to const memory
    char const *buffer; // equivalent to previous declartion
    char * const buffer = {0}; // const pointer to non-const memory
    char * buffer const = {0}; // error
    const char * const buffer = {0}; // const pointer to const memory
    

    Now what about the use of const with a pointer of pointer declaration?

    char **x; // no const;
    const char **x;
    char * const *x;
    char * * const x;
    const char * const * x;
    const char * * const x;
    const char * const * const x;
    

    And what is an easy rule to read those declarations? Which declarations make sense?

    Is the Clockwise/Spiral Rule applicable?

    Two real world examples

    The method ASTUnit::LoadFromCommandLine uses const char ** to supply command line arguments (in the llvm clang source).

    The argument vector parameter of getopt() is declared like this:

    int getopt(int argc, char * const argv[], const char *optstring);
    

    Where char * const argv[] is equivalent to char * const * argv in that context.

    Since both functions use the same concept (a vector of pointers to strings to supply the arguments) and the declarations differ - the obvious questions are: Why do they differ? Makes one more sense than the other?

    The intend should be: The const modifier should specify that the function does not manipulate strings of this vector and does not change the structure of the vector.

  • James Kanze
    James Kanze about 11 years
    @Vorac A frequently cited simplification, which only works for a few simple cases.
  • Vorac
    Vorac about 11 years
    James Kanze, please, if you find the time, explain this in the quesion I opened
  • maxschlepzig
    maxschlepzig almost 9 years
    @bobc, I wrote 'is to read them from right to left' - i.e. this is a reverse iteration. Thus, the choices of 'next' and 'previous' are correct. You seem to interpret those adjective in forward iterative sense - which does not make any sense in that context/paragraph. Hope that clears it up. Why do you think that this platform is crap? Because of answers like mine? Or comments like yours?