4

I am writing a function using fork and execv to replace system() and popen() in a project. I found that although execv won't change the content of the command array, it declare it's second argument as char * const argv[], which can actually change the content of char*. Thus I decide to declare my function like this:int execute_cmd(const char* const cmd[]) so that both the pointer to char is const and the content the const pointer point to is const(Sorry I know this state is confusing but I tried) However, when I call it in this way:

void execute_cmd(const char* const cmd[])
{
    for (int i = 0; cmd[i]; i++)
    {
        printf("%s ", cmd[i]);
    }
}

int main()
{
    char* const cmd[] = {"ls", "-al", 0};
    execute_cmd(cmd);
    return 0;
}

there is a complie error saying that:

test.c:21: warning: passing argument 1 of ‘execute_cmd’ from incompatible pointer type

test.c:10: note: expected ‘const char * const*’ but argument is of type ‘char * const*’

I wonder why this happen because IMO an formal argument with const can take an non-const variable as it's actual parameter, such as:

void printInt(const int);
//which can call like this
int a=10;
printInt(a);

And I think initialize a const variable with a non-const variable is very common, So why this function work but my execute_cmd() didn't? Did I miss something importent with the const? I have search "char * const * and const char* const *" and "const formal argument with non-const actual parameter" such things but haven't get something very useful, I will also appreciate if anyone can give me an idea about the correct key-work to this question. I am not a native English speaker so if there are some grammar problem please forgive me.

Eric
  • 53
  • 4
  • 1
    Try `char * const cmd[] = {"ls", "-al", 0};` – chux - Reinstate Monica Nov 03 '20 at 12:39
  • 1
    "which can actually change the content of char*" --> Code should not attempt to change `"ls"`, etc. – chux - Reinstate Monica Nov 03 '20 at 12:41
  • 1
    @chux-ReinstateMonica Yes, it was `char * const cmd[]`, I wrote it wrong – Eric Nov 03 '20 at 12:56
  • 1
    You are confusing top-level `const` such as `const int`, with the `const` on the pointed-to object such as `char const*`. Location relative to pointer asterisk is of fundamental importance. It's correct that you can't just pass a pointer that says it won't modify the pointed-to object, to a function that declares itself as taking a pointer that might. The rest is just a question of how to work around bad `const`-correctness of APIs or C itself. – underscore_d Nov 03 '20 at 12:56
  • 1
    In an other sense, if I try to call it in C++ code, and part of the cmd or arguments are strings, when initialize like `char* const cmd[] = {ls_string.c_str(), al_string.c_str(), 0}` won't work because c_str() return a const char* – Eric Nov 03 '20 at 13:05
  • 1
    @underscore_d thanks, but "It's correct that you can't just pass a pointer that says it won't modify the pointed-to object, to a function that declares itself as taking a pointer that might" seems just opposite to my situation, I'm trying to pass a array of mutable strings to a function that declares itself will not modify the string. – Eric Nov 03 '20 at 13:20
  • 1
    I always found this unintuitive as well, but there's a problem case this is meant to prevent. You can find some info [here](http://c-faq.com/ansi/constmismatch.html). – Tom Karzes Nov 03 '20 at 13:21
  • 1
    "expected 'const char * const *' but argument is of type 'char const'." is inconsistent with code `char * const cmd` and `char * const cmd[]`. Post true code and true error message. Avoid typos, use cut/paste. – chux - Reinstate Monica Nov 03 '20 at 13:46
  • 1
    @TomKarzes Thanks, When I do search I have found the answer your mentioned, and saw the example, but my question is slightly different with that one: the difference between `char **` and `char * const *` will make step4 in the example illegle to change a const pointer. – Eric Nov 03 '20 at 13:48
  • 2
    @chux-ReinstateMonica have post the minimize code can cause the problem. and true gcc's message – Eric Nov 03 '20 at 14:06
  • @Eric The case you mentioned in your comment is not the same as the case that's giving you an error. If you have `char **x;` and `char *const *y;`, then you can assign `x` to `y` with no error or warning. The problem is the `const` at the deeper level. There are multiple `const` in your post, but only one at the deeper level is causing a problem. – Tom Karzes Nov 03 '20 at 15:24

1 Answers1

1

I wonder why this happen because IMO an formal argument with const can take an non-const variable as it's actual parameter

It is more like "a formal parameter with const can take an non-const variable as it's actual argument."

const char* const cmd_paramter[] is like an "array of const pointer to const char".

char* const cmd_argument[] is an "array of const pointer to char".

Notice the type of the array elements differ: const char * vs. char *.

Same problem with simpler code: "expected 'char *' but argument is of type 'const char *'"

void foo(char *bob);
const char *alice = "Alice";

// Bad, as `foo()` needs to be able to write `*bob`, yet alice point to `const` data.
foo(alice);  

Further, cmd array elements should be of type const char * to prevent writing a string literal.

    // char* const cmd[] = {"ls", "-al", 0};
    const char* const cmd[] = {"ls", "-al", 0};
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    The "same problem with simpler code" code sample is off-topic. OP is clearly aware one cannot assign const to non-const; the question is about the opposite direction. I think the second part of your answer is the best advice so far: if possible, make everything const. – Ruud Helderman Nov 03 '20 at 15:10