为什么argc不是常数?


104
int main( const int argc , const char[] const argv)

正如有效的C ++项目#3指出“尽可能使用const”一样,我开始思考“为什么不设置这些“常量”参数const”?

在程序中是否存在argc修改的值的情况?


38
您可以自由声明argcconst
奥利弗·查尔斯沃思

14
我已经看到许多执行此操作的生产质量代码:--argc
Aniket Inge 2013年

2
我认为这与C遗留问题有关,但不知道如何做。
路易斯·马丘卡

13
我怀疑“尽可能使用const”是指通过引用传递的东西,一旦函数退出,函数内的任何修改都会保留。对于通过值传递的事物(例如整数)而言,这是不正确的,因此,通过使它无法获得任何好处const。确实,传递argc为一个const int方法意味着您不能再将其argc用作函数内部的计数器。
史蒂夫·梅尔尼科夫

4
@SteveMelnikoff:我不同意通过const传递值传递参数没有任何好处。参见例如stackoverflow.com/a/8714278/277304stackoverflow.com/a/117557/277304
leonbloy 2013年

Answers:


114

在这种情况下,历史是一个因素。C将这些输入定义为“非常量”,并且与现有C代码(很大一部分)的兼容性是C ++的早期目标。

某些UNIX API(例如getopt)实际上会进行操纵argv[],因此也不能const出于这个原因而制造它。

(另外:有趣的是,尽管getopt的原型表明它不会修改,argv[]但可能会修改指向的字符串,但Linux手册页指出了getopt它的参数是不正确的,并且看起来他们知道自己在调皮。)群组未提及此排列。)

constargcargv不会买太多,它会导致一些老派的编程实践,例如:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

我已经用C编写了这样的程序,我知道我并不孤单。我从某个地方复制了示例。


1
-1关于“某些UNIX API,例如getopt,实际上确实会操纵argv [],因此也由于这个原因也不能使其成为const”。人们可以随意添加顶层constargv,在不影响任何东西的C函数可以做。另外,getopt被声明为int getopt(int argc, char * const argv[], const char *optstring);。而且这里的const不是顶层,而是将指针声明为const,表示不会修改它们的承诺(尽管它们指向的字符串可能会被修改)。
干杯和健康。-Alf 2013年

@ Cheersandhth. -阿尔夫:getopt 的置换argv[]默认阵列,但不会修改字符串。(permute一词直接来自手册页。)这意味着argv数组本身const即使指向的字符串也不是。排列argv[]数组如何不对其进行操作?
Joe Z

1
抱歉,我查看的文档不一致:签名不允许这种排列。参见示例。但是,以下文本表示确实存在置换。所以,我要删除反对票。
干杯和健康。-Alf 2013年

1
也就是说,您将必须进行一些编辑才能“解锁”,这样我才能删除下降投票。不,您会误解原型。看一下我提供的示例和编译错误“分配只读位置”。
干杯和健康。-Alf 2013年

1
@ Cheersandhth.-Alf:有趣的是...我在标头中查找了原型,它在char* const* ___argv那里,但是实际上接口遵循const char **。如此混乱。我混淆了的优先级[]*关于const。我很抱歉。
Joe Z

36

C标准(ISO / IEC 9899:2011)指出:

5.1.2.2.1程序启动

¶1在程序启动时调用的函数名为main。该实现没有为此函数声明任何原型。它应使用返回类型int且不带参数来定义:

int main(void) { /* ... */ }

或带有两个参数(尽管可以使用任何名称,因为它们在声明它们的函数中是局部的,所以在这里称为argcargv):

int main(int argc, char *argv[]) { /* ... */ }

或同等学历; 10)或其他实现定义的方式。

¶2如果声明了它们,则main函数的参数应遵循以下约束:

  • 的值argc应为非负数。
  • argv[argc] 应为空指针。
  • 如果值argc是大于零,阵列成员argv[0]通过 argv[argc-1]包容应包含指向字符串,其通过前程序启动主机环境给定实现所定义的值。目的是从宿主环境中的其他位置向程序提供在程序启动之前确定的信息。如果主机环境不能提供带有大写和小写字母的字符串,则实现应确保以小写形式接收字符串。
  • 如果的值argc大于零,则所指向的字符串argv[0] 表示程序名称;argv[0][0]如果程序名称在主机环境中不可用,则为空字符。如果值argc大于一,字符串由指向argv[1]通过argv[argc-1] 表示该程序的参数。
  • 数组所指向的参数argcargv字符串argv应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。

10)因此,int可以用定义为的typedef名称代替int,或者argv可以将其写为 char **argv,等等。

注意最后一个要点。它说,双方argcargv应修改。它们不必修改,但可以修改。


有趣。在半相关的注释中,我遇到了一个导致崩溃的错误,事实证明这是因为Qt的QApplication(argc,argv)构造函数argc通过reference 定义的。这让我感到惊讶。
HostileFork表示不要信任2014年

23

argc通常不是常数,因为main()pre-date 的函数签名const

由于argc是一个堆栈变量,因此更改它不会影响您自己的命令行处理。

当然,const如果需要,您可以自由声明它。


8

const形式参数的顶级不是函数类型的一部分。您可以根据需要添加或删除它:它仅影响您可以对函数实现中的参数执行的操作。

因此,argc您可以自由添加一个const

但是因为argv您不能在const不更改功能签名的情况下制作字符数据。这意味着它不是标准main函数签名之一,也不必被识别为main函数。所以,这不是一个好主意。


main在非玩具程序中不使用标准参数的一个很好的理由是,在Windows中,它们无法表示实际的程序参数,例如具有国际字符的文件名。这是因为在Windows中,按照非常严格的约定将它们编码为Windows ANSI。在Windows中,您可以根据GetCommandLineAPI函数实现一些更可移植的参数访问工具。


从加总结,没有什么能够阻止你constargc,但最有用const的-ness argv会给你一个非标准的main功能,最有可能不承认。令人高兴的是(有讽刺意味的),我们没有理由将标准main参数用于可移植的严肃代码。很简单,在实际操作中,它们仅支持旧的ASCII,仅包含英语字母。


1
如果您正在使用cygwin或其他可以正确支持UTF-8的libc为Windows开发(据我所知,cygwin是目前唯一的一个,但是我有人在另外一个选项上工作),则标准main签名可以正常工作,您可以接收任意的Unicode参数。
R .. GitHub停止帮助ICE 2013年

@R在大多数* nix平台上也可以正常工作。问题不在于它们是否在其中工作。但是,这是非常好的倡议,而且碰巧的是,我正在或多或少认真地做同样的事情
干杯和hth。-Alf 2013年

4

的签名main有点像来自的历史文物C。历史上C没有const

但是,const由于const的作用仅是编译时,因此您可以声明参数。


当您说“历史C”时,我们在这里谈论的历史如何?
乔Z

8
const已添加到C89 / C90;在此之前,这不是C.的一部分
乔纳森莱弗勒

@JonathanLeffler:你知道的,我已经忘记了。我于1992年学习C。在此之前,我是Pascal,BASIC和汇编黑客。
Joe Z

2

因为argc是局部变量(在C ++中不是引用或其他变量),并且因为特殊的位置main意味着向后兼容,所以恶作剧赋予了它大量的回旋余地,而没有令人信服的理由迫使应用程序使其成为const。

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

这些和许多其他变体将在各种C和C ++编译器上进行编译。

因此,最终并不是argc不是const,只是它不一定必须是const,但如果您希望它可以是,就可以。

http://ideone.com/FKldHF,C示例:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c,C++示例

main(const int argc) {}

注意没有显式返回类型的变量在C99中不再有效,更不用说C11了,尽管出于向后兼容的原因,编译器继续允许它们。C ++编译器不应接受没有返回类型的变体。
乔纳森·莱夫勒

@JonathanLeffler是的,但是最后一个ideone示例(ideone.com/m1qc9c)是带有的G ++ 4.8.2 -std=c++11。Clang也接受它,但确实给出warning: only one parameter on 'main' declaration [-Wmain],MSVC仅抗议缺少返回类型说明符和缺少对argc的引用。同样,“ shenanigans”和“
leeway

1

除了历史原因外,保持argc和argv不变的一个很好的原因const是编译器实现不知道要对main参数进行什么处理,它只是知道必须给您这些参数。

在定义自己的函数和关联的原型时,您知道可以创建哪些参数const以及函数将修改哪些参数。

极端地说,您可以声明应声明所有函数的所有参数const,然后,如果您有理由更改它们(例如,递减索引以搜索数组),则必须使局部非const变量并将const参数值复制到这些变量中。这使得工作繁忙和额外的LOC并没有真正的好处。如果您不修改参数的值,则可以使用一个不错的静态分析器,建议您将其设置为参数const

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.