argv中指向字符串的指针是否可修改?

10
最近(截至2016年1月,如果问题足够长久)我们有一个问题:“argv中的字符串可修改吗?”。在回答的评论区中,我们(@2501和我)争论的是,到底是可修改的字符字符串(例如字符为**argv)或指向这些字符串的指针(例如指针为*argv).
适当的标准引用来自C11标准草案N1570,§5.1.2.2.1 / 2:

argcargv参数以及argv数组指向的字符串都应该可以被程序修改,并且在程序启动和终止期间保留其最后存储的值。

所以,指向由 argv 指向的字符串的指针是否可修改呢?

1
@nsilent22 如我在引用的问题中已经说过的,在C语言中,你也可以这样做char* str = "foo";,而*str = 'c';是未定义行为。因此,在这里我会把const放在一边。 - cadaniluk
3
argv 是一个局部变量,因此无论如何都是可以修改的。但问题是关于 *argv 和其他指针的。 - too honest for this site
2
@Olaf 嗯,几乎所有的挑剔问题和关于标准的问题都源自不良风格和边缘情况,不是吗?标准是否在托管实现上定义了 int main(void)int main(int, char**) - cadaniluk
1
@nsilent22:; 警告是一种症状。他们发出警告的原因是写入字符串字面值是未定义行为。问题确切地问是否将写入*argv也是UB。 (请注意,标准并未声明字符串字面值为const char [],而只是写入它是UB。这使得字面值仅在技术上为const char [])。 - too honest for this site
4
我认为这与此有关:https://dev59.com/YV8e5IYBdhLWcg3wdqGT?rq=1 - Giorgi Moniava
显示剩余8条评论
2个回答

10
作为OP在问题中引用的,C11标准明确指出argcargv变量以及被argv数组指向的字符串是可修改的。这些指针是否可修改,是需要考虑的问题。标准似乎没有明确说明它是可修改还是不可修改的。
关于标准措辞,有两个关键点需要注意:
  1. If the pointers were supposed to be immutable, the standard could have made it clear by requiring main to be declared as int main(int argc, char *const argv[]), as haccks mentioned in another answer to this question.

    The fact that nowhere in the standard is const mentioned in association with argv seems deliberate. That is, the lack of const does not seem optional, but dictated by the standard.

  2. The standard calls argv consistently an array. Modifying an array refers to modifying its members. Thus, it seems obvious that the wording in the standard refers to modifying the members in the argv array, when it states that argv is modifiable.

    On the other hand, array parameters in C (based on C11 draft N1570, §6.7.6.3p7) "shall be adjusted to 'qualified pointer to type'". Thus, the following code,

    int foo(int x[2], int y[2])
    {
        if (x[0] > y[0])
            x = y;
        return x[1];
    }
    

    is valid C11, since x and y are adjusted to int *x and int *y, respectively. (This is also reiterated in C11 draft N1570, §6.3.2.1p3: "... array ... is converted to an expression with type 'pointer to type' that points to the initial element of the array ...".) Obviously, the same would not be, if x and y were declared as local or global arrays, not function parameters.

就语言律师主义而言,我会说标准没有明确说明,尽管它暗示指针也应该是可修改的。因此,作为对OP的回答:两者都可以。
实际上,指向 argv 数组的指针是可修改的,这在编程中有着悠久的传统。许多库都有初始化函数,接受指向 argcargv 数组的指针,其中一些会修改 argv 数组中的指针(删除特定于库的选项),例如 GTK+ 的 gtk_init()MPI_Init()(尽管至少 OpenMPI 明确表示不检查或修改它们)。寻找参数声明 (int *argc, char ***argv);假设意图是使用 (&argc, &argv)main() 调用,则唯一的原因是修改指针,解析并从命令行参数中删除特定于库的命令行参数,根据需要修改 argcargv 中的指针。

我最初认为POSIX中的getopt()工具依赖于可修改指针的特性--这个特性可以追溯到1980年,被大多数Unix系统采用,并在1997年被标准化为POSIX.2--但这是不正确的,正如Jonathan Leffler在评论中指出的那样:POSIX getopt()不会修改实际的指针;只有GNU getopt()会在POSIXLY_CORRECT环境变量未设置时才会进行修改。无论是GNU getopt_long()还是BSD getopt_long()都会在没有设置POSIXLY_CORRECT的情况下修改指针,但它们比getopt()年轻得多,也不如后者普及。

在Unix领域,修改由argv []数组指向的字符串内容被认为是“可移植”的,并且可以在进程列表中看到修改后的字符串。这种做法的一个例子是DJB的daemontools包中的readproctitle。(请注意,只有通过原地修改,而不能扩展字符串,更改才能在进程列表中显示。)
所有这些都表明了一个非常悠久的传统,基本上,几乎自C语言诞生以来就存在,并且肯定先于C的标准化,将argcargvargv数组中的指针以及这些指针所指向的字符串内容视为可修改的。
由于C标准的目的不是定义新行为,而是在实现之间编码现有行为(以促进可移植性和可靠性等),因此可以安全地假设,标准编写者未明确指定argv数组中的指针为可修改是一种无意的遗漏。其他任何行为都会破坏传统,并且明显违反POSIX标准(该标准旨在促进跨系统的可移植性,并扩展了ISO C标准中未包含的C特性)。

1
ж ҮеҮҶзҡ„getopt()еҮҪ数并дёҚдҫқиө–дәҺеҸҜдҝ®ж”№зҡ„argv; GNU getopt()еҲҷдјҡж”№еҸҳеҸӮж•°еҲ—иЎЁгҖӮ - Jonathan Leffler
@JonathanLeffler:糟糕,确实如此!我甚至检查了一些旧的Unix getopt() 实现,它们也保持参数列表不变。只有GNU getopt() 修改指针。(尽管GNU和BSD的getopt_long()修改指针,即使它们被标记为 const,除非设置了POSIXLY_CORRECT环境变量。)我需要更正我的答案。 - Nominal Animal
我用GTK+和MPI初始化函数替换了@JonathanLeffler指出的关于getopt()的错误部分(以及其他库初始化函数的签名,您可以寻找)。如果您(或任何人)发现任何其他错误,请指出。 - Nominal Animal
1
点1的const参数被反驳了,因为const是在C语言中的main()函数之后才添加的。通过添加const来改变main()的签名将会破坏现有的代码。单独对点2进行修改可能已经足够了。 - chux - Reinstate Monica
字符串字面值可能是不可变的,但它们的类型是 char *,而不是 const char *。C++ 在那方面进行了更改(自2006年左右开始),但 C 没有。 - Kaz
1998年曾经讨论过此事。请查看https://www.open-std.org/jtc1/sc22/wg14/www/docs/n849.htm(查找所有与“argv”有关的引用)。 - Kaz

0
指针是否可修改取决于指针的常量性。参数argv声明为char *argv[]char **argv。这取决于环境是否将其视为char *const argv[](我不知道有没有这样的情况)。

3
仅从知道指针不是const,您就能够断言指针是可写的吗?请参考我在此问题的评论 - cadaniluk
@cad; 你在一个关于指针修改的问题中提到了一个问题,而不是它所指向的内容:*argv中的字符串指针是否可修改?* char const *achar *const a 具有不同的含义。 - haccks
1
但是你回答的前两句话告诉我什么呢?它们基本上告诉我,字符串指针是可修改的,因为它们被相应地声明了。所以你的意思是它们的可修改性取决于实现? - cadaniluk
1
如果你声明 char * p = "abcd",那么你不能执行 p[0] = 'e',但是你可以修改指针 p 本身。p = "Hello"。但是,如果在某些环境中,无论如何,char * p = "abcd" 被解释为 char * const p,那么就不可能修改 p - haccks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接