I have the following program.
#include <stdio.h>
int main(int argc, char const * const argv[], char const * const * envp) {
for (int i = 0; i < argc; i++) {
printf("argv[%d]: %s\n", i, argv[i]);
}
printf("\n");
for (int i = 0; *envp != NULL; i++, envp++) {
printf("envp[%d]: %s\n", i, *envp);
}
return 0;
}
This compiles as expected with no warnings.
If I make the envp pointer const, by changing its declaration to the following.
char const * const * const envp
Then compilation fails as follows.
$ gcc -Wall main.c
main.c: In function ‘main’:
main.c:9:45: error: increment of read-only parameter ‘envp’
9 | for (int i = 0; *envp != NULL; i++, envp++) {
|
This makes sense to me: In this program we can declare that the (first and only first?) character value of each environment string is a constant, we can declare that each pointer to each environment string is constant, but we cannot declare that the char** envp is a constant.
Now, my question is, how could I declare the array variable argv
a constant in a similar way, so that argv
cannot be reassigned?
That is, I want to declare argv
in a such a way that the following would produce a similar compilation error to the "read-only parameter ‘envp’"
error above. I want the following to fail to compile. As is, the following compiles.
#include <stdio.h>
int main(int argc, char const * const argv[], char const * const * envp) {
char const * const argvbad[1] = { NULL };
argv = argvbad; // I want this to be impossible
for (int i = 0; i < argc; i++) {
printf("argv[%d]: %s\n", i, argv[i]);
}
printf("\n");
for (int i = 0; *envp != NULL; i++, envp++) {
printf("envp[%d]: %s\n", i, *envp);
}
return 0;
}
This is a question about array variables in general. I want to learn how to declare array variables as constant, so that the array that they refer to cannot be reassigned.
In a parameter declaration, an array can be declared with qualifiers such as const
inside the brackets, [
and ]
:
void foo(const char * const argv[const]) { … }
When the declaration is automatically adjusted to make the declared identifier be a pointer, the qualifiers will be applied to it. The above is equivalent to void foo(const char * const * const argv)
.
Note that int main(int argc, char const * const argv[], char const * const * envp)
is not generally appropriate for main
because the C standard specifies the forms that the declaration of main
should take, in C 2018 5.1.2.2.1 1. Its specification is flexible in that it allows implementations to accept additional forms, but I am not aware of any implementation documenting that main
may have that form, with the additional qualifiers.
An additional complication is that qualifiers on parameters, directly, are irrelevant for function compatibility. For example, these are compatible declarations:
void foo(int *p);
void foo(int * const p);
This is because C 2018 6.7.6.3 15 says:
… (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
However, these would not be compatible:
void foo(int *p);
void foo(const int *p);
since the above passage applies only to qualifiers directly on the parameters. This raises the possibility that int main(int argc, char *argv[const])
is a satisfactory declaration of main
per C 2018 5.1.2.2.1 1, since it is compatible with int main(int argc, char *argv)
and hence may satisfy its allowance of “equivalent” declarations.