3

I'm trying to make arguments information umodifiable.

#include <stdio.h>
#include <stdlib.h>

int main(const int argc, const char* const argv[]) {
  //argc = 1;         // error: assignment of read-only parameter 'argc'
  //argv[0] = "argv"; // error: assignment of read-only location '*argv'
  //argv[0][0] = 'a'; // error: assignment of read-only location '**argv'
  return EXIT_SUCCESS;
}

Now when I do this,

argv = NULL; // no compile-time error

the compiler makes silence.

What does the statement actually do? How can I prohibit my codes from doing that?

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • 3
    Arguments are already unmodifiable.. They're passed to your application from the command line using `argv`, and `argc` indicates the number that were passed. You don't have to do anything to make them unmodifiable except to stop writing code that tries to do so. IOW, if you stop writing code like `argv =`, you won't have any problems. – Ken White Apr 29 '18 at 04:42
  • 4
    @KenWhite "Arguments are already unmodifiable", this is false, you are allowed to change it. – Stargateur Apr 29 '18 at 04:50
  • 6
    You have answers, but they're missing it's **not a good idea at all**. The C standard only describes two valid forms for main: `int main(void)` and `int main(int argc, char *argv[])`. `int main(int argc, char **argv)` is 100% equivalent because of type adjustment rules, therefore ok as well, but if you start adding `const`, it's a **different signature**. Implementations of C are allowed to support other forms of main they define themselves, but your program no longer conforms to standard C, another compiler could reject it. –  Apr 29 '18 at 05:22
  • @FelixPalmen I've never known that. Is it true that putting `const`s makes ***different signature***? Thanks. – Jin Kwon Apr 29 '18 at 05:29
  • @FelixPalmen: The two `const`s in `const char* const argv[]` are definitely nonstandard, but off the top of my head, I don't recall whether making `argc` or `argv` themselves `const` is nonstandard. – user2357112 Apr 29 '18 at 05:29
  • @JinKwon see a standard document (e.g. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf, the latest draft to C11) -- §5.1.2.2.1 desribes `main` and e.g. §6.7.6.1 p2 tells you why `const char **` and `char **` are **not compatible**. –  Apr 29 '18 at 05:32
  • @user2357112 by *themselves*, you mean like `int main(const argc, char ** argv const)`? I **guess** that would be allowed as it doesn't change how the function can be called **at all** (and it's IMHO utterly pointless, just making your local variable `const`). –  Apr 29 '18 at 05:34
  • 2
    What's with all the downvotes? It's a good question? – StoryTeller - Unslander Monica Apr 29 '18 at 06:15
  • @FelixPalmen: incompatible means no silent conversion (6.5.16.1p1) but 'same representation and alignment' (6.2.5p26,28) are 'meant to imply interchangeability' in several (admittedly nonnormative) footnotes. Since `main` must be callable from C as well as the automatic call from the environment, I believe the last sentence of 6.7.6.3p15 allows top-level `const` – dave_thompson_085 Apr 29 '18 at 06:32
  • @dave_thompson_085 I'm on mobile right now, so can't look up the text ... are you saying there's a guarantee in the standard that a pointer to const has the same internal representation as the corresponding pointer to non-const? In this case, yes, it should work (might be an interesting discussion in language-lawyer). I'd still say don't do it, if only to avoid confusion... –  Apr 29 '18 at 06:51

3 Answers3

10

Well, first of all, don't do it.

I refer to existing answers for how you would do it, they explain the different levels const can be applied and how to write it in straight pointer syntax as well as in "disguised as an array"-syntax. That's definitely good to know.

But here it comes: main is very special. According to the C standard, it doesn't have a prototype, but the definition should take one of two forms only. Here's the original text, from N1570, the latest draft to C11:

§ 5.1.2.2.1:

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent;10) or in some other implementation-defined manner.

The footnote 10 even explains what equivalent means here:

Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as char ** argv, and so on.

But as for adding some consts, look for example at § 6.7.6.1 p2:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

(emphasis mine). const is a type qualifier. So const char ** is not compatible with char **. You define a main that doesn't conform to the C standard any more. Therefore, just don't do it. Use const correctness inside your program, but don't try changing the interface for program startup.


Side note: exactly the one const you're asking about here might be ok, because it applies to the pointer itself, which is just a local variable to the function (as parameters are always by value in C function calls). So it doesn't change the function's interface. That's why in practice, nobody bothers adding such consts. It's not important for calling code whether a function modifies its locals or not.

8

Since this answer has been pinned to the top, I feel I should point out that my answer only addresses part of the issue. See Felix Palmen's answer for why not to declare main this way.


If a parameter is declared with an array type, the type is implicitly replaced with a pointer type:

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

becomes

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

so argv is a non-const pointer to const pointer to const char.

argv = NULL simply sets that pointer to a null pointer, just like assigning NULL to any other pointer. This won't have any directly visible external effect - it won't erase your command line or anything - but it will interfere with further attempts to use argument information from within your program.

If you want argv itself to be const, declare it as const:

int main(const int argc, const char * const * const argv) {
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    Could be write `int main(int const argc, char const * const * const argv)`, however there is not a consensus about what syntax use. – Stargateur Apr 29 '18 at 05:02
  • 2
    I like writing `const char *const *const argv` because `*const` acts like a unit ("const pointer"). It's almost like a subscript on `*`. – melpomene Apr 29 '18 at 05:05
  • @chqrlie No, I don't. I realize my choices are mine alone, but please don't tell me what I "really mean". – melpomene May 01 '18 at 12:25
  • 1
    @melpomene: sorry for my poor choice of terms. I personally prefer to underscore what `const` applies to this way: `char const* const* const argv` – chqrlie May 01 '18 at 12:41
6

You can put const in the brackets of an array-looking pointer parameter to prevent reassignment to it:

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

People don’t typically bother with this.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • I'm still not sure why they bothered putting in this syntax. C's declaration syntax is confusing enough without it, and as far as I can tell, it doesn't add new functionality or make anything easier. – user2357112 Apr 29 '18 at 04:48
  • 2
    @user2357112: It needs to be available if you want to use both `const` and `[static n]`, I think. – Ry- Apr 29 '18 at 04:49
  • This doesn't help if one does [as many programs do]: `parse_my_options(&argc,&argv)`. And, I believe I read somewhere where the correct definition(s) is/are: `int main(int argc,char **argv)` or `int main(void)` or `int main(int argc,char **argv,char **envp)`. Others will work, but they're not considered idiomatic. See: https://stackoverflow.com/questions/12146594/definition-of-main-in-c – Craig Estey Apr 29 '18 at 04:53
  • @CraigEstey: Doesn’t help how? You get a const pointer from `&argv`. – Ry- Apr 29 '18 at 04:54
  • @CraigEstey This have nothing to do with idiom, the standard only say that any other definition of main will be implemented behavior. But it's true that main argument have no reason to be `const` because we often use them to handle option. As in [`getopt()`](http://man7.org/linux/man-pages/man3/getopt.3.html), this function change the order of pointer in `*argv`. – Stargateur Apr 29 '18 at 05:00
  • Huh. I was not aware of the `[static n]` thing. [Looks like](https://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p7) it's a way to say that a pointer must point to the start of an at-least-n-element array (which has confusing implications if you pass a pointer that points into the middle of an array). – user2357112 Apr 29 '18 at 05:05
  • From the C standard [1999], 5.1.2.2.1: _The parameters argc and argv and the strings pointed to by the argv array shall be **modifiable** by the program, and retain their last-stored values between program startup and program termination._ So, adding `const` sort of defeats the intended purpose. – Craig Estey Apr 29 '18 at 05:12
  • @user2357112 "which has confusing implications if you pass a pointer that points into the middle of an array" it's a pointer anyway, there no way to pass an array to a function in C. So you just need to pass a pointer where that address a contigus `n` numbers of element. So it could be a pointer to the middle of an array. More information about effect of [static] [What is the purpose of static keyword in array parameter of function?](https://stackoverflow.com/questions/3430315/what-is-the-purpose-of-static-keyword-in-array-parameter-of-function-like-char#3430353.) – Stargateur Apr 29 '18 at 05:18
  • @Stargateur: The standard specifically requires the pointer to point to the *start* of an array. If I declare `int f(int a[static 2])` and `int x[3]` and call `f(x+1)`, it's quite confusing whether or not that call is valid. – user2357112 Apr 29 '18 at 05:22
  • @user2357112 Well... standard often use array and contigus element interchangeably... This is one point where the standard really sux. But you can easily see with [this](http://rextester.com/NBWXZ45522) example that it's not an array ;). The only and very useful warning produce is: "sizeof on array function parameter will return size of 'char *' instead of 'char [static 100]' [-Wsizeof-array-argument]" ;). To conclude when the standard say array, it's often mean contigus element. – Stargateur Apr 29 '18 at 05:29
  • If you are going to do this, make it really obscure like this: `int main(int const argc, char const* const argv<:const:>)` ;-) – chqrlie May 01 '18 at 06:36