C语言 argv[n]可写吗?

nzk0hqpo  于 2023-01-08  发布在  其他
关注(0)|答案(5)|浏览(117)

C11 5.1.2.2.1/2规定:
参数argcargv以及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;
我可以看到(至少)两种可能的论证:

  • 上面的引用特意提到了argvargv[x][y],但没有提到argv[x],因此其意图是不可修改
  • argv是指向非const对象的指针,因此在没有相反的特定措辞的情况下,我们应该假设它们是可修改对象。
izj3ouym

izj3ouym1#

IMO,像argv[1] = "123";这样的代码是UB(使用原始的argv)。
“参数argcargv以及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的缺失也不能完全指定argvargv[]、或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规范详细说明了可以修改的内容:argcargvargv[][],则有理由质疑argv[]是否可修改,因为规范中没有声明代码可以修改它。
考虑到main()的特殊性和省略指定argv[]为可修改的,保守的程序员会将这种灰色视为UB,等待未来C规范的澄清。
如果argv[i]在给定平台上是可修改的,那么i的范围当然不应超过argc-1
由于“argv[argc]应为空指针”,因此将argv[argc]分配给NULL以外的对象似乎是违规的。
虽然字符串是可修改的,但代码不应超过原始字符串的长度。

char *newstr = "abc";
if (strlen(newstr) <= strlen(argv[1])) 
  strcpy(argv[1], newstr);

1 C17/18没有变化。因为那个版本是为了澄清很多事情,它重新执行了这个规范是足够的,没有遗漏一个“argv数组元素应该是可修改的”。

hec6srdp

hec6srdp2#

argv数组并不一定是可修改的(但在实际实现中可能是)。这是一个有意的措辞,在1998年的n849会议上得到了重申:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n849.htm

PUBLIC REVIEW COMMENT #7

[...]

Comment 10.
Category: Request for information/clarification
Committee Draft subsection: 5.1.2.2.1
Title: argc/argv modifiability, part 2
Detailed description:

Is the array of pointers to char pointed to by argv modifiable?

Response Code: Q
    This is currently implictly unspecified and the committee 
    has chosen to leave it that way.

此外,还提出了两项单独的建议,分别修改和增加措辞。两项建议都被拒绝。感兴趣的读者可以通过搜索“argv”找到它们。

w8biq8rn

w8biq8rn3#

argc仅仅是int,并且可以不受任何限制地修改。
argv是一个可修改的char **。这意味着argv[i] = x是有效的。但是它没有说任何关于argv[i]本身是可修改的。所以argv[i][j] = c导致未定义的行为。
C标准库的getopt函数确实修改了argc和argv,但从不修改实际的char数组。

jm81lzqq

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,歧义就会消失。

ia2d9nvy

ia2d9nvy5#

其中明确提到argvargv[x][x]是可修改的。如果argv是可修改的,那么它可以指向char数组的另一个第一个元素,因此argv[x]可以指向另一个字符串的第一个元素。最终,argv[x]也是可修改的,这可能是标准中没有必要明确提到它的原因。

相关问题