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

正如Effective C++ Item#3 所说的“尽可能使用 const”,我开始思考“为什么不制作这些‘常量’参数const?”。

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

4

7 回答 7

118

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

某些 UNIX API,例如getopt,实际上确实可以操作argv[],因此也不能因此而制作const它。

(旁白:有趣的是,虽然getopt's 的原型表明它不会修改argv[]但可能会修改指向的字符串,但 Linux 手册页表明它改变了getopt它的参数,并且看起来他们知道他们很顽皮。Open 的手册页组没有提到这种排列。)

穿上const它不会买太多argcargv它会使一些老式的编程实践失效,例如:

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

我用 C 编写过这样的程序,而且我知道我并不孤单。我从某处复制了这个例子。

于 2013-12-13T03:13:06.477 回答
38

C 标准 (ISO/IEC 9899:2011) 说:

5.1.2.2.1 程序启动

¶1 程序启动时调用的函数名为main. 实现没有声明这个函数的原型。它应定义为返回类型为 int 且不带参数:

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

或带有两个参数(这里称为argcand argv,尽管可以使用任何名称,因为它们对于声明它们的函数是本地的):

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

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

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

  • 的值argc应为非负数。
  • argv[argc]应为空指针。
  • 如果 的值argc大于零,则argv[0]通过 argv[argc-1]inclusive 的数组成员应包含指向字符串的指针,这些指针在程序启动之前由宿主环境赋予实现定义的值。目的是从托管环境中的其他地方向程序提供在程序启动之前确定的信息。如果主机环境不能提供大写和小写字母的字符串,则实现应确保以小写形式接收字符串。
  • 如果 的值argc大于零,则 指向的字符串argv[0] 代表程序名;argv[0][0]如果主机环境中没有程序名,则应为空字符。如果 的值argc大于 1,则argv[1]through指向的字符串argv[argc-1] 表示程序参数。
  • 数组所指向的参数argc和字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。argvargv

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

注意最后一个要点。它说两者都argc应该argv是可修改的。它们不必修改,但可以修改。

于 2013-12-13T03:25:19.187 回答
23

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

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

当然,const如果你愿意,你可以自由地声明它。

于 2013-12-13T03:13:27.567 回答
8

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

所以对于argc你可以自由添加添加一个const.

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


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


总而言之,没有什么能阻止您添加constto argc,但最有用的const-ness onargv会给您一个非标准main功能,很可能不会被识别为这样。令人高兴的是(以一种讽刺的方式)有充分的理由将标准main参数用于可移植的严肃代码。很简单,在实践中它们只支持旧的 ASCII,只有英文字母。

于 2013-12-13T04:23:31.070 回答
4

的签名main有点像C. 历史上C没有const

但是,您可以声明您的参数const,因为 const 的效果仅在编译时起作用。

于 2013-12-13T03:13:42.780 回答
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,只是它不一定是,但如果你愿意,它可以是。

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

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

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

main(const int argc) {}
于 2013-12-13T05:50:43.840 回答
1

除了历史原因之外,保留 argc 和 argv 非的一个很好的理由const是编译器实现不知道您将如何处理 main 的参数,它只知道它必须给您这些参数。

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

走极端,你可以声明所有函数的所有参数都应该被声明const,然后如果你有理由改变它们(例如减少索引以搜索数组),你必须使局部非const变量并将const参数的值复制到这些变量中。这使得忙碌的工作和额外的 LOC 没有真正的好处。如果您不修改参数的值,一个不错的静态分析器会选择,并建议您制作参数const

于 2013-12-13T19:32:27.303 回答