cad*_*luk 8 c pointers argv language-lawyer
最近(2016年1月,如果问题持续时间足够长)我们有一个问题argv中的字符串是否可以修改?.
在这个答案的评论部分,我们(@ 2501和我)争论它是否真的是可修改的字符串(一个示例字符**argv)或指向字符串的指针(示例指针是*argv).
适当的标准报价来自C11标准草案N1570,§5.1.2.2.1/ 2:
数组指向的参数
argc和argv字符串argv应由程序修改,并在程序启动和程序终止之间保留它们最后存储的值.
那些argv可修改指向字符串的指针是什么?
正如在问题中引用的OP,C11标准明确指出argc和argv变量以及argv数组指向的字符串是可修改的.无论这些指针是否可修改,问题都在眼前.该标准似乎没有明确地以某种方式陈述它.
关于标准中的措辞,有两点需要注意:
如果指针应该是不可变的,那么标准就可以通过要求main声明为明确int main(int argc, char *const argv[]),就像在另一个问题的答案中提到的haccks一样.
在标准中没有const提到的事实argv似乎是故意的.也就是说,缺乏const看似不是可选的,而是由标准决定的.
标准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];
}
Run Code Online (Sandbox Code Playgroud)
是有效的C11,因为x和y分别调整为int *x和int *y.(这也在C11草案N1570,§6.3.2.1p3中重申:"......数组......被转换为类型'指向类型'的表达式,指向数组的初始元素......". )显然,如果x并且y声明为本地或全局数组,而不是函数参数,则不会相同.
就语言 - 律师主义而言,我会说标准并没有以这种或那种方式陈述,尽管它暗示指针也应该是可修改的.因此,作为OP的答案:两者兼而有之.
在实践中,argv阵列中的指针有一个很长的传统可以修改.许多库都有初始化函数,它们指向argc指向argv数组的指针和指向数组的指针,其中一些库确实修改了argv数组中的指针(删除了特定于库的选项); 例如GTK +gtk_init()和MPI_Init()(尽管至少OpenMPI明确声明它不检查或修改它们).寻找参数声明(int *argc, char ***argv); 唯一的原因 - 假设意图是从main()使用中调用(&argc, &argv)- 是修改指针,从命令行参数解析和删除库特定的命令行参数,修改两者argc和指针argv作为需要.
(我最初声称getopt()POSIX 中的设备依赖于可修改的指针 - 可追溯到1980年的特征,大多数Unix风格采用,1997年在POSIX.2中标准化 - 但这是不正确的,正如Jonathan Leffler指出的那样在注释中:POSIX getopt()不修改实际的指针;只有GNU才会这样做getopt(),并且只有在POSIXLY_CORRECT未设置环境变量的情况下才会修改.除非设置了GNU getopt_long()和BSD getopt_long(),否则GNU 和BSD都会修改指针POSIXLY_CORRECT,但它们比较年轻且不太广泛getopt().)
在Unix领域,它被认为是"可移植的"来修改argv[]数组指向的字符串的内容,并且在进程列表中可以看到修改后的字符串.这是如何有用的一个例子是DJB的daemontools包,readproctitle.(请注意,字符串必须就地修改,并且无法扩展,以使更改在进程列表中可见.)
所有这一切都表明了很长的传统,基本上差不多,因为C的诞生作为编程语言,和C的标准化肯定前,治疗中argc,argv中,在指针argv数组,字符串的内容,将这些指针,可修改的.
因为C标准的目的不是定义新的行为,而是编写跨实现的现有行为(以提高可移植性和可靠性等),似乎可以安全地假设它是部分标准编写者的意外遗漏而不是显式地将argv数组中的指针指定为可修改.任何其他东西都会破坏传统,并明确违背POSIX标准(它也旨在促进跨系统的可移植性,并扩展未包含在ISO C标准中的C功能).