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

最近(2016年1月,如果问题持续时间足够长)我们有问题argv中的字符串是否可修改? 。
在这个答案的评论部分,我们(@ 2501和我)认为它是否真的是可修改的字符串 (例如**argv的示例字符)或指向字符串指针 (示例指针是*argv )。

适当的标准报价来自C11标准草案N1570,§5.1.2.2.1/ 2:

参数argcargv以及argv数组指向的字符串应由程序修改,并在程序启动和程序终止之间保留它们最后存储的值。

argv modifiable指向字符串的指针是什么?

正如在问题中引用的OP,C11标准明确指出argcargv变量以及argv数组指向的字符串是可修改的。 无论这些指针是否可修改,问题都在眼前。 该标准似乎没有明确地以某种方式陈述它。

关于标准中的措辞,有两点需要注意:

  1. 如果指针应该是不可变的,那么标准可以通过要求main声明为int main(int argc, char *const argv[])来明确 ,就像在这个问题的另一个答案中提到的haccks一样 。

    标准中没有任何地方与argv联合提到的事实似乎是故意的。 也就是说,缺少const似乎不是可选的,而是由标准决定的。

  2. 该标准始终将argv称为数组。 修改数组是指修改其成员。 因此,很明显标准中的措辞是指修改argv数组中的成员,当它声明argv是可修改的时。

    另一方面,C中的数组参数 (基于C11草案N1570,§6.7.6.3p7) “应调整为’限定类型’的指针” 。 因此,以下代码,

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

    是有效的C11,因为xy分别调整为int *xint *y 。 (在C11草案N1570,§6.3.2.1p3中也重申了这一点: “……数组……被转换为类型’指向类型的指针’的表达式,指向数组的初始元素……” 。 )显然,如果xy被声明为局部或全局数组,而不是函数参数,则不会相同。

就语言 – 律师主义而言,我会说标准并没有以某种方式陈述它,尽管它暗示指针也应该是可修改的。 因此,作为OP的答案: 两者兼而有之


在实践中, argv数组中的指针有一个很长的传统可以修改。 许多库都有初始化函数,它们带有指向argc的指针和指向argv数组的指针,其中一些argv确实修改了argv数组中的指针(删除了特定于库的选项); 例如GTK + gtk_init()MPI_Init() (尽管至少OpenMPI明确声明它不检查或修改它们)。 寻找参数声明(int *argc, char ***argv) ; 唯一的原因 – 假设意图是使用(&argc, &argv)main() (&argc, &argv) – 是修改指针,从命令行参数中解析和删除特定于库的命令行参数,根据需要修改argcargv的指针。

(我最初声明POSIX中的getopt()工具依赖于可修改的指针 – 可追溯到1980年的特征,大多数Unix风格采用,1997年在POSIX.2中标准化 – 但这是不正确的,如Jonathan Leffler在评论中指出:POSIX getopt()不会修改实际指针;只有GNU getopt()getopt() ,而且只有在没有设置POSIXLY_CORRECT环境变量时才POSIXLY_CORRECT getopt_long()和BSD getopt_long()修改指针除非设置了POSIXLY_CORRECT ,但与getopt()相比,它们更年轻,更不普遍。)

在Unix领域,它被认为是“可移植的”来修改argv[]数组指向的字符串的内容, 并且在进程列表中可以看到修改后的字符串 。 这是如何有用的一个例子是DJB的daemontools包, readproctitle 。 (请注意,字符串必须就地修改,并且无法扩展,以使更改在进程列表中可见。)

所有这些都表明了一个非常悠久的传统,基本上几乎从C作为编程语言诞生以来,并且肯定在C的标准化之前,处理argcargvargv数组中的指针以及指向的字符串的内容。那些指针,可修改。

因为C标准的目的不是定义新的行为,而是编写跨实现的现有行为(以提高可移植性和可靠性等),似乎可以安全地假设它是部分标准编写者的意外遗漏而不是显式指定argv数组中的指针为可修改的。 任何其他东西都会破坏传统,并明确违背POSIX标准(它也旨在促进跨系统的可移植性,并扩展未包含在ISO C标准中的Cfunction)。

指针是否可修改取决于指针的常量 。 参数argv声明为char *argv[]char **argv 。 它取决于环境是否将它视为char *const argv[]或不(我不知道)。