为什么将C / C ++主argv声明为“ char * argv []”,而不仅仅是“ char * argv”?


21

为什么要argv声明为“指向数组第一个索引的指针”,而不是仅仅声明为“指向数组第一个索引的指针” char* argv

为什么在这里需要“指针指向”的概念?


4
“指向数组第一个索引的指针”-这不是char* argv[]or 的正确描述char**。那是指向字符的指针。具体来说,外部指针指向数组中的第一个指针,内部指针指向以nul终止的字符串的第一个字符。这里没有索引。
塞巴斯蒂安·雷德尔

12
如果只是char * argv,如何得到第二个参数?
gnasher729

15
将空间放置在正确的位置,您的生活会变得更加轻松。char* argv[]将空间放置在错误的位置。说char *argv[],现在很明显这意味着“表达式*argv[n]是类型的变量char”。不要陷入试图找出什么是指针,什么是指向指针的指针等等的问题。该声明告诉您可以对此事执行哪些操作
埃里克·利珀特

1
char * argv[]与类似的C ++结构进行心理比较std::string argv[],它可能更易于解析。...只是不要开始以这种方式实际编写
贾斯汀时间2恢复莫妮卡

2
@EricLippert注意,该问题还包括C ++,您可以在char &func(int);其中使用例如不&func(5)具有type的示例char
Ruslan

Answers:


59

Argv基本上是这样的:

在此处输入图片说明

左侧是参数本身-实际上已作为参数传递给main的参数。该地址包含一个指针数组的地址。这些参数中的每一个都指向内存中某个位置,该位置包含在命令行上传递的相应参数的文本。然后,在该数组的末尾保证有一个空指针。

请注意,各个参数的实际存储空间至少可能彼此分开分配,因此它们在内存中的地址可能会相当随机地排列(但根据事物的写入方式,它们也可以位于单个连续的块中)。记忆-您根本不知道也不应该在乎)。


52
无论哪种布局引擎为您绘制了该图,它们的最小交叉算法都存在错误!
埃里克·利珀特

43
@EricLippert可能有意强调指出,指示者可能不是连续的,也不是秩序井然的。
jamesdlin

3
我会说这是故意的
Michael

24
当然,这是故意的,我想埃里克可能认为这是对的,但是(正确地,国际海事组织)认为该评论很有趣。
杰里·科芬

2
@JerryCoffin,可能还会指出,即使实际参数在内存中是连续的,它们也可以具有任意长度,因此,对于每个参数,仍然需要不同的指针才能访问它们,argv[i]而无需扫描所有先前的参数。
ilkkachu

22

因为那是操作系统提供的:-)

您的问题是鸡肉/鸡蛋倒置问题。问题不是在C ++中选择想要的东西,而是在OS中用C ++怎么说。

Unix传递一个“字符串”数组,每个字符串都是一个命令参数。在C / C ++中,字符串是“ char *”,因此根据口味,字符串数组为char * argv []或char ** argv。


13
不,这正是“在C ++中选择所需内容的问题”。例如,Windows将命令行作为单个字符串提供,但C / C ++程序仍接收其argv数组-运行时负责标记命令行并argv在启动时构建数组。
Joker_vD

14
@Joker_vD我想在这扭曲的方式什么操作系统给你。具体来说:我猜C ++之所以这样做,是因为C是这样做的,而C之所以这样做是因为当时C和Unix有着千丝万缕的联系,而Unix就是这样。
Daniel Wagner

1
@DanielWagner:是的,这是来自C的Unix遗产。在Unix / Linux上,最小的_start调用main只需要传递main一个指向argv内存中现有数组的指针即可。格式正确。内核将其从argv参数复制execve(const char *filename, char *const argv[], char *const envp[])到为启动新可执行文件而进行的系统调用。(在Linux上,argv [](数组本身)和argc在进程条目的堆栈上。我认为大多数Unix是相同的,因为这是一个好地方。)
Peter Cordes

8
但Joker的观点是,C / C ++标准将其留给了args的实现。他们不必直接来自操作系统。在传递扁平字符串的OS上,良好的 C ++实现应包括标记化,而不是设置argc=2和传递整个扁平字符串。(仅遵循标准字母不足以使它有用;它故意为实现选择留出了很多空间。)尽管某些Windows程序将要特别处理引号,所以实际的实现确实提供了一种获取扁平字符串的方法,太。
Peter Cordes

1
Basile的答案几乎是此+ @Joker的更正以及我的评论,还有更多详细信息。
Peter Cordes

15

首先,作为参数声明char **argvchar *argv[]; 相同。它们都暗示着一个指向字符串的指针(一个数组或一个或多个可能的集合)。

接下来,如果您只有“指向char的指针”(例如,只有)char *,则为了访问第n个项目,您将必须扫描前n-1个项目以找到第n个项目的开始。(并且这还要求每个字符串都连续存储。)

使用指针数组,您可以直接索引第n个项目-因此(虽然并非绝对必要-假设字符串是连续的),但通常更方便。

为了显示:

./程序你好世界

argc = 3
argv[0] --> "./program\0"
argv[1] --> "hello\0"
argv[2] --> "world\0"

在os提供的字符数组中,可能是:

            "./program\0hello\0world\0"
argv[0]      ^
argv[1]                 ^
argv[2]                        ^

如果argv只是“ char的指针”,您可能会看到

       "./program\0hello\0world\0"
argv    ^

但是(尽管可能是由os的设计决定的)并不能真正保证三个字符串“ ./program”、“hello”和“ world”是连续的。此外,这种“指向多个连续字符串的单个指针”是一种更不寻常的数据类型构造(用于C),尤其是与指向字符串的指针数组相比。


如果不是的,argv --> "hello\0world\0"你有argv --> index 0 of the array(你好),就像一个正常的阵列。为什么这不可行?然后您继续读取数组argc时间。那么您传递的是argv本身,而不是argv的指针。
用户

@auser,这就是argv->“ ./program\0hello\0\world\0”是:指向第一个字符的指针(即“。”)。如果您将指针移到第一个\ 0之后,那么您有一个指向“ hello \ 0”的指针,然后指向“ world \ 0”。ARGC次(命中\ 0" )后,大功告成当然,它可以工作进行,正如我说,一个不寻常的结构。
埃里克Eidt

您忘记声明在您的示例中argv[4]NULL
Basile Starynkevitch

3
(至少在最初)有保证argv[argc] == NULL。在这种情况下argv[3],不是argv[4]
Miral

1
@Hill,是的,谢谢,因为我试图明确说明空字符终止符(并错过了那个)。
Erik Eidt

13

为什么将C / C ++主argv声明为“ char * argv []”

可能的答案是因为C11标准 n1570(在§5.1.2.2.1程序启动中和C ++ 11标准 n3337(在§3.6.1主要功能中要求在托管环境中使用(但请注意,C标准提到了也§5.1.2.1独立环境)另请参见

下一个问题是,为什么C和C ++标准选择main具有这样的int main(int argc, char**argv)签名?的解释主要是历史:Ç用发明的Unix,其中有一个,其不通配符做之前fork(这是一个系统调用来创建的过程)和execve(其是系统调用执行程序),并且execve发送一个数组字符串程序参数,与main已执行程序的。阅读有关Unix哲学ABI的更多信息。

C ++努力遵循C的约定并与其兼容。它不能定义main为与C传统不兼容。

如果您从头开始设计了一个操作系统(仍然具有命令行界面),并从头开始为其设计了一种编程语言,则可以自由发明不同的程序启动约定。其他编程语言(例如Common Lisp或Ocaml或Go)具有不同的程序启动约定。

实际上,main是由一些crt0代码调用的。请注意,在Windows上,每个程序都可以用crt0等效的方式进行切换,并且某些Windows程序可以通过非标准WinMain入口点启动。在Unix上,通配符由外壳程序完成(并且crt0正在适应ABI以及它指定的初始调用堆栈布局,以适应C实现的调用约定)。


12

它不是将其视为“指向指针的指针”,而是将其视为“字符串数组”,分别[]表示数组和char*字符串。运行程序时,可以向其传递一个或多个命令行参数,这些参数将反映在参数中main::argc是参数的计数,并argv允许您访问各个参数。


2
+1这个!在许多语言中-bash,PHP,C,C ++-argv是字符串数组。关于这一点,您必须在看到char **或时加以思考char *[],这是相同的。
rexkogitans

1

在许多情况下,答案是“因为这是标准”。引用C99标准

—如果argc的值大于零,则数组成员argv [0]至argv [argc-1](含)应包含指向字符串的指针,这些指针在程序启动前由主机环境赋予实现定义的值。

当然,它已经被标准化之前它已经在由K&R C在早期Unix实现使用中与存储命令行参数的目的(你在Unix的照顾壳如/bin/bash/bin/sh在嵌入式系统中,但不是)。引用K&R的第一版“ The C Programming Language”(第110页)

第一个(通常称为argc)是调用程序时使用的命令行参数的数量;第二个(argv)是指向包含参数的字符串数组的指针,每个字符串一个。

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.