argv = NULL 是什么意思?

3

我正在尝试使参数信息不可修改。

#include <stdio.h>
#include <stdlib.h>

int main(const int argc, const char* const argv[]) {
  //argc = 1;         // error: assignment of read-only parameter 'argc'
  //argv[0] = "argv"; // error: assignment of read-only location '*argv'
  //argv[0][0] = 'a'; // error: assignment of read-only location '**argv'
  return EXIT_SUCCESS;
}

现在当我这样做时,

argv = NULL; // no compile-time error

编译器保持沉默。

这个语句实际上是做什么的? 我如何禁止我的代码做这件事?


3
参数已经是不可修改的。它们是使用argv从命令行传递到您的应用程序中的,而argc表示传递的数量。您不必采取任何措施使它们不可修改,除非停止编写试图修改它们的代码。也就是说,如果停止编写像argv =这样的代码,就不会有任何问题。 - Ken White
4
“@KenWhite的说法‘Arguments are already unmodifiable’是错误的,你可以对其进行修改。” - Stargateur
6
你有答案,但它们缺少了"这根本不是一个好主意"的含义。C标准只描述了main函数的两种有效形式:int main(void)int main(int argc, char *argv[])int main(int argc, char **argv)由于类型调整规则而完全等效,因此也可以接受,但如果你开始添加const,那么这就是一个不同的签名。C语言的实现可以支持他们自己定义的其他形式的main函数,但你的程序将不再符合标准C,另一个编译器可能会拒绝它。 - user2371524
2
所有的踩是怎么回事?这不是一个好问题吗? - StoryTeller - Unslander Monica
@dave_thompson_085 我现在正在使用手机,所以无法查找文本...你是说标准中保证指向const的指针与相应的非const指针具有相同的内部表示吗?在这种情况下,是的,它应该可以工作(可能会引起语言律师之间的有趣讨论)。我仍然建议不要这样做,即使只是为了避免混淆... - user2371524
显示剩余5条评论
3个回答

10

首先,不要这样做

关于如何做到这一点,我提到了现有的答案,它们解释了可以应用const的不同级别,以及如何在直接指针语法伪装为数组语法中编写。那肯定是很好知道的。

但是问题来了:main非常特殊。根据C标准,它没有原型,但定义只能采用两种形式之一。以下是原始文本,来自N1570,C11的最新草案:

§ 5.1.2.2.1:

程序启动时调用的函数名为main。实现不声明此函数的原型。它应该被定义为具有返回类型int且没有参数:
int main(void) { /* ... */ }
或具有两个参数(这里称为argcargv,尽管可以使用任何名称,因为它们是在声明它们的函数中局部的):
int main(int argc, char *argv[]) { /* ... */ }
或等效方式;10)或以其他某种实现定义的方式。
甚至脚注10还解释了这里的等效意味着什么:

因此,int可以替换为定义为int的typedef名称,或者argv的类型可以写为char ** argv等。

但是,如果要添加一些const,例如查看§ 6.7.6.1 p2

要使两个指针类型兼容,两者都必须是完全相同的限定符,并且都必须是指向兼容类型的指针。

(我加粗了). const是一种类型限定符。因此,const char **char **不兼容。如果你定义了一个不符合C标准的main,那么就不要这样做。在程序内部使用const修饰,但不要尝试改变程序启动的接口。


顺便提一下:你在这里问到的那个唯一的const可能是可以的,因为它应用于指针本身,而指针只是函数内部的一个局部变量(因为C函数调用中参数总是按值传递)。因此,它不会改变函数的接口。这就是为什么在实践中,没有人费心添加这样的const。对于调用代码来说,函数是否修改其局部变量并不重要。


8

由于这个答案已经被置顶,我觉得我应该指出我的回答只解决了问题的一部分。请参考Felix Palmen的回答,了解为什么不要以这种方式声明main


如果使用数组类型声明参数,则该类型会自动替换为指针类型:

int main(const int argc, const char* const argv[]) {

变成

int main(const int argc, const char* const *argv) {

所以,argv 是一个指向常量指针的非常量指针。

argv = NULL 只是将该指针设置为 null 指针,就像将 NULL 赋值给任何其他指针一样。这不会有任何直接可见的外部影响 - 它不会擦除您的命令行或任何东西 - 但它会干扰进一步尝试从程序内部使用参数信息。

如果你想让 argv 本身成为常量,将其声明为 const:

int main(const int argc, const char * const * const argv) {

1
可以写成int main(int const argc, char const * const * const argv),但是关于使用哪种语法还没有达成共识。 - Stargateur
2
我喜欢编写 const char *const *const argv,因为 *const 就像一个单元(“const 指针”)。它几乎就像是 * 上的下标。 - melpomene
@chqrlie,不,我没有。我意识到我的选择只属于我自己,但请不要告诉我我“真正的意思”。 - melpomene
1
@melpomene:对不起,我的措辞不够准确。我个人更喜欢用下划线来强调 const 的应用方式:char const* const* const argv - chqrlie

6
你可以将 const 放在类似数组的指针参数的括号中,以防止对其进行重新赋值:
int main(const int argc, const char* const argv[<b>const</b>]) {

通常人们不会关注这个。


我仍然不确定为什么他们要加入这个语法。C的声明语法已经够令人困惑了,而据我所知,它并没有增加新功能或使任何事情变得更容易。 - user2357112
2
如果你想同时使用 const[static n],我认为它需要是可用的。 - Ry-
@CraigEstey:这对什么没有帮助?你从&argv得到一个常量指针。 - Ry-
@user2357112 嗯...标准通常将数组和连续元素混用...这是标准真正糟糕的一点。但你可以通过这个例子轻松看出它不是一个数组 ;). 唯一且非常有用的警告是:"sizeof on array function parameter will return size of 'char *' instead of 'char [static 100]' [-Wsizeof-array-argument]" ;). 总之,当标准说数组时,通常意味着连续元素。 - Stargateur
如果你要这么做,就让它变得非常晦涩,就像这样:int main(int const argc, char const* const argv<:const:>) ;-) - chqrlie
显示剩余6条评论

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