0

I'm learning C and still a little green but I'm going through some lessons and I am getting a compiler warning about a duplicate 'const' declaration specifier.

the offending line is;

int main(int argc, const char const *argv[])

Unless I grossly misunderstand the way const works in a parameter definition, since argv is an array I would need to use;

int main(int argc, const char const **argv)

or perhaps

int main(int argc, const char *argv[])

The compiler doesn't complain with either of these options, and if my thinking is correct both should produce the same result since *argv[] is a pointer to an array. I realize they would have different meanings if argv were NOT an array, but am I wrong that they produce the same result and that they are the proper declaration as opposed to the original, in this particular case?

Jack Galt
  • 83
  • 8
  • 2
    `argv` can be modified, it's not required to be `const`, and maybe you are looking for `const char *const pointer` where the first const means that the pointed to data is `const` and the second means that the pointer itself is const, i.e. It cannot point to anything else but it's original value. – Iharob Al Asimi Feb 17 '16 at 19:02
  • 1
    The first two snippets are identical, and produce exactly the same warning. – user3386109 Feb 17 '16 at 19:03
  • 2
    No, technically they are not identical. The first snippet is a an array of `char *`, the second is a double pointer. – Gillespie Feb 17 '16 at 19:12
  • 2
    @RPGillespie You have a lot to learn. Using the first snippet (without the consts), try `for (int i = 0; i < argc; i++) printf( "%s\n", *argv++ );` and see what the compiler has to say about that (nothing), and whether it works (it does). Point is, even though the argument is specified as an array of pointers, it is actually a double pointer. – user3386109 Feb 17 '16 at 19:20
  • @user3386109: This is true in this particular case, with `argv`, but RPGillespie is right. In theory, `char **p;` and `char *p[]` are two different things. – fanton Feb 17 '16 at 19:23
  • 3
    @fanton No, not when `char *p[]` appears as a function argument. In other contexts, sure. – user3386109 Feb 17 '16 at 19:24
  • Take a look at the answer to this question: http://stackoverflow.com/questions/7526152/easy-rule-to-read-complicated-const-declarations – nsilent22 Feb 17 '16 at 19:59
  • 3
    @RPGillespie The C Standard defines that they are exactly identical, in a function parameter list. It is not possible to pass arrays by value in C – M.M Feb 17 '16 at 20:10
  • 1
    `const char const` is the same as `const const char`, in every situation. – user253751 Feb 17 '16 at 20:40
  • ... and the same as `const char` and `char const`, too, of course. – John Bollinger Feb 17 '16 at 20:46
  • Thanks for all the feedback. I know that *[] and ** are different when they aren't in a function argument, but my question was specifically whether or not they were producing an identical result in THIS case. – Jack Galt Feb 18 '16 at 07:06

3 Answers3

3

In this line:

int main(int argc, const char const *argv[])

you wrote const twice. In general, this is permitted by the C Standard but it seems your compiler is being friendly and warning anyway. You could either ignore or disable this warning, or remove one of the consts.

However, main is special: using const here is non-standard. It should be:

int main(int argc, char *argv[])

Compilers may or may not accept the version with const. It is implementation-defined whether they do, which means the compiler's documentation should have a page talking about which signatures of main it accepts.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • I realize that the syntax is non standard for a normal int main definition, I actually didn't write the code, it's a lesson I'm working through that someone else wrote. I'm still working though the whole program to understand the reasons for a lot of things done in the program. I suspect the const modifiers are there because argv is being used by APR functions, but I haven't gotten that far yet. Are there not certain instances where const modifiers are used **both** before the type and before the pointer of said type? – Jack Galt Feb 18 '16 at 07:13
  • @JackGalt no, these are both the same context (order of qualifiers and types makes no difference so long as you don't reorder past a `*`, you can even write `const int const long const long const` instead of `const long long` if you want) – M.M Feb 18 '16 at 07:23
  • Thank you! That helps clarify things considerably, and you saved me some time trying to understand what a code typo was doing. If the compiler had rejected it entirely I would've understood immediately it was wrong. I've seen other code examples with double use of const and so I assumed there was reason for it, I've been chasing my tail apparently. – Jack Galt Feb 18 '16 at 08:36
1

Unless I grossly misunderstand the way const works in a parameter definition, since argv is an array I would need to use

int main(int argc, const char const **argv)

or perhaps

int main(int argc, const char *argv[])

Apparently, then, you grossly misunderstand the way const works in a parameter definition. We'll get to that in a moment, but first I observe that the permitted signatures for main() are specified by the standard. Implementations are permitted to support additional signatures, but the ones that conforming, "hosted" implementations are required to support are:

int main(void)

and

int main(int argc, char *argv[])

and equivalents (C2011, 5.1.2.2.1). Regardless of all other considerations, therefore, those signatures are supported, and inequivalent ones, such as with const qualifiers, do not need to be supported.

Back, then, to the question of array arguments. Suppose you have an array ...

int a[4];

... that you want to pass to a function. Technically speaking, you can't do it. In almost all contexts, and particularly when they appear as function arguments, array values decay to pointers. You can pass such a pointer, as is indeed common, but you cannot pass the array itself. The element type of the array, including exactly the same qualifiers, is the target type of the resulting pointer value. Moreover, values cannot be const -- that's a property of types. Thus, it's perfectly fine to write

int a[4];
int f(int *);

f(a);

Additionally, be aware that both your proposed method signatures declare parameter argv as a pointer to a pointer to const char. That type is not compatible with pointer to pointer to char. If you wanted to declare that argv itself cannot be changed (though doing so is unnecessary) then you could write it as

int main(int argc, char ** const argv)

, which I suspect your compiler would accept. If you wanted to declare that the pointers in the argv array cannot be changed via argv, then that would be ...

int main(int argc, char * const *argv)

... which also stands a good chance of being accepted. What you actually tried to write, however, declared the contents of the argv elements to be const, and that's not compatible.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

const applies to what is on its immediate left, unless there is nothing to the left then it applies to what is on its immediate right instead. So the declaration:

int main(int argc, const char const *argv[])

Is applying two const to the same char identifier, hence the warning.

If you want to say that argv is an array of non-constant pointers to constant characters, use this:

const char * argv[]

If you want to say that argv is an array of constant pointers to constant characters, use this:

const char * const argv[]

The correct declaration of argv parameter is simply char* argv[] or char** argv. No const specifiers are used at all. Specific compilers may allow const variations, but that is not defined by the standards.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770