main()的第二个参数的char*argv[]和char**argv之间的差异

main()的第二个参数的char*argv[]和char**argv之间的差异,c,arrays,pointers,parameter-passing,argv,C,Arrays,Pointers,Parameter Passing,Argv,代码1 #include<stdio.h> int main(int argc, char *argv[]) { int j; printf("%d", argv[1][0]); return 0; } #包括 int main(int argc,char*argv[]) { int j; printf(“%d”,argv[1][0]); 返回0; } 代码2 #include<stdio.h> int main(int argc, char **argv) {

代码1

#include<stdio.h>

int main(int argc, char *argv[])
{
int j;
printf("%d", argv[1][0]);
return 0;
}
#包括
int main(int argc,char*argv[])
{
int j;
printf(“%d”,argv[1][0]);
返回0;
}
代码2

#include<stdio.h>

int main(int argc, char **argv)
{
int j;
printf("%d", argv[1][0]);
return 0;
}
#包括
int main(int argc,字符**argv)
{
int j;
printf(“%d”,argv[1][0]);
返回0;
}

代码1和代码2都提供相同的输出。但是代码1和代码2中主函数的参数2不同。指针数组是在编译时在数据段上方创建的。argv是指针数组。然后我们应该在main函数中将参数声明为指向字符的指针,即**argv。在代码1中声明如何正确?

对于c来说,char**xchar*x[]是表达同一事物的两种方式,这一点很重要。两者都声明参数接收指向指针数组的指针。回想一下,你总是可以写:

 char *parray[100];
 char **x;

 x = &parray[0];

然后同样使用x。

基本上,char*argv[]表示字符指针数组,而char**argv表示指向字符指针的指针

在任何数组中,数组的名称都是指向数组第一个元素的指针,也就是说,它包含第一个元素的地址

在下面给出的代码中,在char数组x中,x是指向第一个元素“1”的指针,它是一个字符。所以它是指向一个字符的指针

在数组arr中,arr是指针第一个元素x,它本身就是指向字符的指针。所以它是指向另一个指针的指针

因此,x是char*,arr是char**

在函数中接收某些内容时,基本规则是,您必须告诉所接收内容的类型。所以,要么你简单地说你想要接收一个char**,要么你也可以说char*arr[]

在第一种情况下,我们不需要考虑任何复杂的事情。我们只知道,我们正在接收一个char*数组。难道我们不知道吗?所以,我们接受并使用它

在第二种情况下,它很简单,正如我在上面解释的,arr是一个char**,您可以将其作为其类型并安全地接收它。现在系统知道了我们收到的内容的类型,我们可以通过简单地使用数组注释来访问下一个元素。这就像,我们已经收到了数组的起始地址,我们当然可以转到下一个元素,因为我们知道它的类型,我们知道它包含什么以及如何进一步使用它。我们知道它包含指向char的指针,所以我们也可以合法地访问它们

void func1(char* arr[])
{
    //function body
}
void func2(char** arr)
{
    //function body
}

int main()
{
    //x, y and z are pointer to char
    char x[3]={'1', '2', '3'};
    char y[3]={'4', '5', '6'};
    char z[3]={'7', '8', '9'};

    //arr is pointer to char pointer
    char* arr[3]={x, y, z};

    func1(arr);
    func2(arr);
}

他们完全一样。C11标准的§5.1.2.2.2规定:

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

int main(void) { /* ... */ }
或者使用两个参数(此处称为
argc
argv
) 可以使用任何名称,因为它们是它们所在函数的本地名称 声明如下:

或同等品;10) 或者在其他定义的实现中 态度

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

显然,这两个声明的目的是相同的。除此之外,该规则在§6.7.6.3/7中有描述:

应调整作为“类型数组”的参数声明 指向“”的限定指针 类型“”,其中类型限定符(如果有)是在数组类型派生的
[
]
中指定的限定符


[编辑]在发表评论时使用GCC可能是GCC 7.2

声明这样的数组

char array[]
使其为常量,这意味着您不能拥有以下代码

char array[] = "hello";
array = "hey";
即使第二个字符串更小并且应该适合,您也会得到此错误

错误:数组类型“char[6]”不可分配

如果您有
**argv
,您可以编写

main(int argc, char **argv)
{
    char **other_array;
    /*
     * do stuff with other_array
     */
    argv = other_array;
}
如果您有
*argv[]
,则

main(int argc, char *argv[])
{
    char **other_array;
    /*
     * do stuff with other_array
     */
    argv = other_array;
}
给你这个警告

警告:从“char**”分配给“const char**”将丢弃嵌套指针类型中的限定符


因此,从技术上讲,这是一个小的优化,就像您编写了
const

一样,第二个参数对于编译器来说没有任何不同,即
char*argv[]
只是等价于
char**argv
。这更像是首选编码风格的问题。除了使用IMO中的
sizeof
之外,如果您至少简要地注意到C中有一些地方(如Ed Heal提到的
sizeof
)数组不等同于指针,并且“数组调整”是函数中的参数声明(问题所问)与表达式中的“数组衰减”不同(尽管相关)。对于参考资料,您可以引用例如和@Ilmari Karonen IMHO,该答案是正确的(但示例是错误的)。仅仅因为函数签名中有大括号,并不意味着它将是一个数组变量!事实是,C假设它是一个普通指针!这甚至是唯一一个可以从C中得到的逻辑决策,因为如果真的假设一个数组变量,那么就没有任何东西可以分配给它(数组变量没有像指针那样针对自身的单独内存)。所以我相信这只是C提供的一个简化符号(chararr[]),它与另一个符号(char*arr)完全相同。这个答案中有很多误解,已经混淆了。首先,
char*argv[]
char**argv
是绝对等效的,并没有任何不同的含义。其次,
arr
不是指针。我没有仔细研究这篇文章来找出其他具体的问题,但这些问题已经很严重,需要投反对票,并真诚地希望你能在其他人收到我的邮件之前重温这篇文章
main(int argc, char *argv[])
{
    char **other_array;
    /*
     * do stuff with other_array
     */
    argv = other_array;
}