Are the strings in argv modifiable?

2020-08-09 07:43发布

问题:

I just wrote a small program that reads command line arguments in C, nothing too difficult. I was also modifying them, for example changing the first character of the parameter to uppercase.

I know that you shouldn't modify string literals as it can cause undefined behavior, so was just wondering if the strings in the *argv[] are literals that you shouldn't change.

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

回答1:

From the C11 standard draft N1570, §5.1.2.2.1/2:

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.

They are modifiable. That means they are not string literals.

But be careful: the upper citation only refers to pointers to strings, excluding the obligatory null pointer at argv[argc]1.
From the C11 standard draft N1570, §5.1.2.2.1/2 (same as above)1:

argv[argc] shall be a null pointer


Notes:

  • Something regarding this sentence:

    I know that you shouldn't modify string literals as it can cause undefined behavior [...]

    "can"? It does always. Undefined behavior includes expected, as if well-defined, and unexpected behavior.


1 Thanks to @black!



回答2:

The arrays that support the strings in argv are modifiable.
But you have no way to know their sizes.

I would frown upon seeing code that (tries to) increase the size of the strings.

#include <stdio.h>
#include <string.h>
// this program may behave erraticaly
int main(int argc, char **argv) {
    for (int k = 1; k < argc; k++) {
        printf("original argv[%d] is %s\n", k, argv[k]);
    }
    printf("\n");
    for (int k = 1; k < argc; k++) {
        strcat(argv[k], " foo"); // add foo to each argv string
        printf("first modification to argv[%d] is %s\n", k, argv[k]);
    }
    printf("\n");
    for (int k = argc; k > 1; k--) {
        strcat(argv[k - 1], " bar"); // add bar to each argv string
        printf("final argv[%d] is %s\n", k - 1, argv[k - 1]);
    }
    return 0;
}

On my machine, calling that program with one two three arguments produces

original argv[1] is one
original argv[2] is two
original argv[3] is three

first modification to argv[1] is one foo
first modification to argv[2] is foo foo
first modification to argv[3] is foo foo

final argv[3] is foo foo bar
final argv[2] is foo foo foo bar bar
final argv[1] is one foo foo foo bar bar bar