main 函数的 argv 参数用 char* argv[ ]还是 char** argv 合适?

2019-07-08 10:33:43 +08:00
 shijingshijing

这个问题其实以前没怎么注意,反正都是指向字符串的指针,但是最近看几个底层库的实现,基本上都是用的

int main(int argc, char** argv)

这种形式进行声明

以前没怎么关注过这个细节,写过

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

好像也经常有

int main(void)

甚至

void main(void)

阿里巴巴的 Java 开发手册里面第 7 条也提到了强制使用 String[] args 而否定了 String args[]这种形式。

Visual Studio 新建项目自动生成的代码也是使用了 string[] args。

想了解一下,这个里面究竟有什么讲究?如果是 c 和 c++的话,是不是一定写成 char** argv 更好?

3993 次点击
所在节点    程序员
33 条回复
shijingshijing
2019-07-08 21:11:47 +08:00
@smdbh 恩,我知道最后都是地址的操作。

@geelaw
说的很清楚了,谢谢。可以理解为阿里的这条标准纯粹是为了强迫症和项目管理、代码评审等目的而特意规定的,而不是除去性能、安全等其他目的,对吗?

@ipwx 我从头到尾都没有说是因为指针什么的,把指针拿来作为出发点吧。我说的 visual studio 生成的代码没指定说是生成的 C++代码吧。

不需要太多的说教,底层我也懂,现在是关注一下这些细节,而且上面我也强调了,特别感兴趣的是阿里为什么强制使用 String[] args,麻烦回复一点有意义的内容,不太喜欢你这种语气,也不喜欢你这样的回复风格,否则请 block 我以及本帖,谢谢。
msg7086
2019-07-08 22:12:38 +08:00
这贴几乎可以当作提问的智慧的教科书般的反面教材了。
标题问 C 的指针和数组,内容说 Java,顺便提到一个无关的 C#,最后再回过头否定自己标题的提问,顺便把回答者批判一番。

还行。我佛了。
shijingshijing
2019-07-08 22:14:58 +08:00
@msg7086

请主动 block,不谢。
msg7086
2019-07-08 23:39:37 +08:00
@shijingshijing 这么教科书式的帖子 Block 了多可惜。
zhao4dick25cm
2019-07-09 07:34:18 +08:00
没必要,少写一点是一点,就是 int main()
jaskle
2019-07-09 07:47:22 +08:00
数组式好理解,c 这种东西,写法千万种
Mithril
2019-07-09 10:22:54 +08:00
@shijingshijing Java 的话数组声明就是两种都支持的,这跟 Main 函数没关系。不管用那种写法,都只是说这参数是个数组而已。
然而这两种写法比较容易混,比如
String[] arr1, arr2[];
arr2 是个 String[][]
官方的说法是这个[]可以出现在声明的最前面,也可以出现在特定变量处。但不推荐两种写法混着写,所以一般代码规范就强制要求使用一种写法。
这个其实不光会影响变量,比如你甚至可以把一个返回数组的方法写成这样:
String method()[]{return new String[1];}
String[] method()[]{return new String[1][1];}
看起来就比较乱
FrankHB
2019-07-09 11:53:14 +08:00
允许 void main 还敢装做是 C 或者 C++的,除非是特定 C 的 freestanding implementation,就是扯蛋。
注意 C 和 C++略有不同。
cf. https://github.com/FrankHB/pl-docs/blob/master/zh-CN/main-function.md

剩下的问题,主要来自 C-like 语法的不成熟的语法设计:
https://github.com/FrankHB/pl-docs/blob/master/zh-CN/about-syntax.md#%E5%85%B3%E4%BA%8E%E5%8E%86%E5%8F%B2%E9%81%97%E7%95%99%E9%97%AE%E9%A2%98%E7%9A%84%E4%BE%8B%E5%AD%90

如果允许,使用 string[] args 这样的风格是更推荐的做法。不过,C 和 C++中根本不允许这种语法……
考虑到 char**本身倾向于造成理解上的含义混乱(凭啥不是 const char**或者 char* const*……,凭啥*表示数组而不是 in/out parameter )而且[]允许字面上更好的可读性(虽然不被语言支持,但起码你能自然地写[/*size=1~3*/]这样暗示源的预先设计的范围),个人建议 char* args[]而避免 char** args。
类似 char**这样的写法,基本上在 C++不应该出现,因为没什么合适的理由使这种潜在有歧义的写法显得有必要。而 C 的 char**可以表达 C++的 char*&这样的情况,约定只有这种情况使用 char**,则用意相对是清楚的。
FrankHB
2019-07-09 12:05:12 +08:00
@geelaw 我不记得有哪个版本的 C/C++ 标准提出“正确的 main 的签名只有以下 2 种”(姑且搁置“签名”是指什么的问题)。请核实并指出来源。
geelaw
2019-07-09 12:40:10 +08:00
@FrankHB #29 OK,更准确的说法是“在任何符合 C/C++ 标准的语言实现中总是正确的 main 的签名只有 2 种”,例如见 n4659 6.6.1.2 和 n1256 5.1.2.2.1.1。

签名,如果不是一个 C/C++ 语言中的概念,就是日常理解的含义,是若干个类型的有序组(返回类型,第一个形参类型,第二个形参类型……)以及一个 bool (是否具有 ... 变长参数)。两个签名相同当且仅当这个 (有序组, bool) 的 pair 相等。
FrankHB
2019-07-09 12:57:40 +08:00
@geelaw 你的说法是错的。之前我给的链接有提供原文引用和分析(新的标准修订版本原则上不会改动这里的内容):
github.com/FrankHB/pl-docs/blob/master/zh-CN/main-function.md
首先的具体问题:你提的“正确”事实上对不上标准中的和一般理解的“正确性”相关的要求(不管是形式语法、以 shall 明确的条款、constraint 还是 well-definedness ),不管是对 conforming implementation 还是对 program 来说。
标准对 conforming implementation 要求需要支持两种的 main 声明,这不表示禁止其它任意的扩展(也不直接禁止程序使用这样的扩展)。例如,ISO C++明确禁止非 int 返回类型的全局 main 函数,但没对参数类型有同样的限制。
你的另外的一个技术错误是无视了 C 和 C++的不同。C 的 int main(void);原型声明对应 C++的 int main();,但 C 的 int main()作为声明并不等同于 int main(void);,在 C++中近似 int main(...);。
函数的签名(signature) 在 ISO C++ 中一直有明确定义(用于支持重载等),不同版本的定义有差别但都排除了返回类型,参见[defns.signature]。
ISO C 没有明确对应的“签名”概念。日常所谓的“签名”,语源是数理逻辑,一般是指“函数类型”,注意这和上面的 C++中的定义有差别——它依赖返回类型(而 C 和 C++ 其实也是有这样的“函数类型”的概念的)。在 C 从 C++ 的设计中照搬来原型声明这个设计之前,“签名”的含义是不怎么明确的。特别地,C 仍支持 () 参数列表这样明确不指定参数类型的情况。所以虽然你说的“日常理解的含义”虽然不算有很大的普遍问题,但这个上下文中反而会引起混乱。
geelaw
2019-07-09 13:15:22 +08:00
@FrankHB #31

“在任何符合 C/C++ 标准的语言实现中总是正确”,我已经加上了全称量词,那么我应该说“在任何……总是被支持”。显然可以造出一个只支持 int main(void) 和 int main(int, char **) 的实现,所以这是惟二“总是正确”(总是被支持)的写法。

我觉得你是知道我知道 C/C++ 对于 () 作为形参列表的不同的 - - 我没有指出 C/C++ 在这方面的区别,是因为我的写法在 C/C++ 中含义一样。( C++ 仍然支持 (void) 表达 () 见于 n4659 11.3.5.4。)

关于签名,我并不知道文档中使用的定义,是否包含返回类型只是一个取决于使用目的的美学选择。不过感谢你指出标准里的定义是什么样的。

最后,我并不知道 C 支持(意思是“任何实现必须支持”而不是“允许这样的扩展”) int main()。
FrankHB
2019-07-09 14:45:22 +08:00
@geelaw 就“在任何……总是被支持”,指的若是程序(strictly conforming program),且限定是 hosted implementation,则你的补充是对的;除了 ISO C 在 Program startup 一节中对 main 有显式允许“ or equivalence ”的说法(其中一个例子就是 argv 的**和[]的等价性,见脚注)这点外。
int main(); 不是任何 ISO C 实现都被要求支持的声明,除非同时存在被要求支持的 main 的定义之一的原型声明。此时,前者是和定义兼容(compatible)的函数声明。
但若 int main()出现在函数定义中,这仍然是要求被支持的,因为 ISO C 的说法是 defined ... with no parameters ... or equivalence,并没说此处的声明必须要有原型或者必须是明确的(void)(甚至没说是 parameter type list )——尽管函数声明符中的()是 obsolescent feature ——所以即便排除原型保证的 diagnostic 的好处,定义中写成 int main(void)仍然更好。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/580911

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX