C11 5.1.2.2.1/2规定:
参数argc
和argv
以及argv
数组指向的字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。
我对这一点的解释是,它规定:
int main(int argc, char **argv)
{
if ( argv[0][0] )
argv[0][0] = 'x'; // OK
char *q;
argv = &q; // OK
}
然而,它没有提到:
int main(int argc, char **argv)
{
char buf[20];
argv[0] = buf;
}
是否允许argv[0] = buf;
?
我可以看到(至少)两种可能的论证:
- 上面的引用特意提到了
argv
和argv[x][y]
,但没有提到argv[x]
,因此其意图是不可修改 argv
是指向非const
对象的指针,因此在没有相反的特定措辞的情况下,我们应该假设它们是可修改对象。
5条答案
按热度按时间izj3ouym1#
IMO,像
argv[1] = "123";
这样的代码是UB(使用原始的argv
)。“参数
argc
和argv
以及argv
数组指向的字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。”C11 dr & C17 dr 1 §5.1.2.2.1 2回想一下,
const
是在C创建很多年之后才进入C的。就像
char *s = "abc";
在它应该是const char *s = "abc";
的时候是有效的一样。对const
的需要并不是必需的,否则太多的现有代码会随着const
的引入而被破坏。同样地,即使今天的
argv
应该被认为是char * const argv[]
或具有const
的某个其他签名,char *argv[]
中const
的缺失也不能完全指定argv
、argv[]
、或argv[][]
。const
的需求需要由规范驱动。从我的阅读,由于规范是沉默的问题,但深入到其他任务的
main()
的argv =
和argv[i][j] =
,这是UB。未定义的行为在本国际标准中用“未定义的行为”或省略行为的任何明确定义”§4 2表示
[编辑]:
main()
是C中一个非常特殊的函数。其他函数中允许的内容在main()
中可能允许,也可能不允许。C规范详细说明了其参数的属性,给出了不需要的签名int argc, char *argv[]
。main()
与C中的其他函数不同,可以有一个替代签名int main(void)
和可能的其他签名。main()
是不可重入的。正如C规范详细说明了可以修改的内容:argc
、argv
、argv[][]
,则有理由质疑argv[]
是否可修改,因为规范中没有声明代码可以修改它。考虑到
main()
的特殊性和省略指定argv[]
为可修改的,保守的程序员会将这种灰色视为UB,等待未来C规范的澄清。如果
argv[i]
在给定平台上是可修改的,那么i
的范围当然不应超过argc-1
。由于“
argv[argc]
应为空指针”,因此将argv[argc]
分配给NULL
以外的对象似乎是违规的。虽然字符串是可修改的,但代码不应超过原始字符串的长度。
1 C17/18没有变化。因为那个版本是为了澄清很多事情,它重新执行了这个规范是足够的,没有遗漏一个“
argv
数组元素应该是可修改的”。hec6srdp2#
argv
数组并不一定是可修改的(但在实际实现中可能是)。这是一个有意的措辞,在1998年的n849会议上得到了重申:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n849.htm
此外,还提出了两项单独的建议,分别修改和增加措辞。两项建议都被拒绝。感兴趣的读者可以通过搜索“argv”找到它们。
w8biq8rn3#
argc
仅仅是int
,并且可以不受任何限制地修改。argv
是一个可修改的char **
。这意味着argv[i] = x
是有效的。但是它没有说任何关于argv[i]
本身是可修改的。所以argv[i][j] = c
导致未定义的行为。C标准库的
getopt
函数确实修改了argc和argv,但从不修改实际的char数组。jm81lzqq4#
答案是argv是一个数组,而且是的,它的内容是可以修改的。
该关键字在同一部分的前面:
如果argc的值大于零,则arraymembers argv[0]到argv[argc-1](含)应包含指向字符串的指针,这些字符串是在程序启动之前由主机环境给定的实现定义值。
从这里可以清楚地看到,argv被认为是一个特定长度的数组(argc),那么 *argv是指向该数组的指针,已经衰减为指针。
在此上下文中阅读,大意为“argv应该是可修改的...并保留其内容”的语句显然意味着该数组的内容是可修改的。
我承认,措辞中仍然存在一些模糊之处,特别是如果修改argc会发生什么情况。
先说清楚,我想说的是我把这句话理解为:
[the] argv [array]的内容和argv数组指向的字符串应该是可以修改的...
所以数组中的指针和它们所指向的字符串都在可读写内存中,修改它们不会造成任何伤害,并且它们的值都在程序的生命周期中保留。我希望这种行为在所有主要的C/C++运行时库实现中都能找到,没有例外。这不是UB。
含糊之处在于对argc的提及,很难想象有任何目的或实现会将argc的值(看起来只是一个局部函数参数)不能改变,那为什么要提呢?标准明确指出,函数可以改变其参数的值,那为什么要在这方面特别对待argc呢,就是这个意外的提到argc才引发了对argv的这个关注,否则它将不加评论地通过。从句子中删除argc,歧义就会消失。
ia2d9nvy5#
其中明确提到
argv
和argv[x][x]
是可修改的。如果argv
是可修改的,那么它可以指向char
数组的另一个第一个元素,因此argv[x]
可以指向另一个字符串的第一个元素。最终,argv[x]
也是可修改的,这可能是标准中没有必要明确提到它的原因。