在C中(我想这也适用于C ++),
之间有什么区别char str[4] = "abc";
char *cstr = {"abc"};
当我尝试将“abc”传递给接受char **的函数时出现问题
void f(char** s)
{
fprintf(stderr, "%s", *s);
}
执行以下操作会产生编译器错误。如果转换为char **(使编译器满意)程序段错误。
f(&str);
但以下工作正常
f(&cstr[0]);
答案 0 :(得分:9)
第一行线定义了一个包含四个字节的数组。这两个是等价的:
char str[4] = "abc";
char str[4] = {'a', 'b', 'c', 0};
第二行声明一个指向内存位置的指针,其中包含字节'a','b','c'和0.这两个是等价的:
char *cstr = {"abc"};
char *cstr = "abc";
您的问题来自混合char[]
和char*
。如果该函数接受char**
,则必须创建char*
以获取以下地址:
char str[4] = "abc";
char *cstr = str;
f(&cstr);
答案 1 :(得分:4)
这是指针和数组在C中不等效的示例。特别是:数组衰减到指针的规则不会递归应用
这意味着数组可以用作指针,但指向数组的指针不能用作指向指针的指针。这就是你在这里遇到的。这就是为什么编译器在你没有显式地转换为char **时会抱怨类型不匹配的原因。那应该是你第一个发现错误的线索。
导致段错误的原因是:数组自动衰减到指针的方式是转换为第一个元素的地址。指向数组的指针同样是指向数组第一个元素地址的指针。因此,指向数组的指针和指向数组的指针是一回事。换句话说,当作为指针传递时,str具有与& str相同的值。因此,如果您尝试将& str设置为指向指针的指针,则它不起作用,因为它只是一个(单级)指针。
例如,
void f(char** pp);
void g(char* p);
char[] str = "abcd"; // Lets say this is allocated at address 0x1234
g(str); // Value of p in g is 0x1234 (by automatic conversion of char[4] to char*)
char* p_str = &str; // Value of p_str is 0x1234
g(p_str); // Value of p in g is again 0x1234
f(str); // Illegal, no conversion of char[] to char** (obvious)
f(p_str); // Illegal, no conversion of char* to char** (obvious)
f(&str); // Illegal, no conversion of char*[4] to char** (less obvious)
f((char**)p_str); // Ok, now you're overriding the typecheck
但是在最后一次调用f((char**)p_str)
之后,f中pp的值仍然是0x1234,因为你没有修改p_str的值,你只是抑制了类型检查器的抱怨。这意味着* pp将是'a',而不是指向包含'a'的地址的指针。这就是为什么当f试图执行** pp时会出现段错误。
答案 2 :(得分:1)
在这里,大括号实际上是一种虚假的痕迹。
str
是一个初始化数组,而不是指针:在你的例子中没有实际指针变量保存str
的地址,所以你不能取其地址,因此表达式&str
会产生编译错误。
相反,cstr
是一个指针变量,它保存字符串常量的地址,并且可以获取该指针变量的地址。
答案 3 :(得分:0)
因此,&x
会为您提供指向x 的指针。但指向数组的指针与数组是一回事。这是由左值表达式规则的一个例外加强的,在&
运算符的情况下,当应用于数组时,表达式类型不会转换为“指向...的指针”。 (6.2.2.1,ANSI 9899-1990。)
这意味着当str
是实际数组时,&str
和str
是相同的。但&p
其中p
实际上是一个指针,会得到一个双重间接指针,即**p
。
如果标准只是说“& before array”,那么这将更加清晰,我认为dmr关于C的出版物曾经说过。相反,ANSI将其短语作为转换规则,远离&
运算符的定义。
答案 4 :(得分:-1)
您可能想尝试
char str[4] = {'a', 'b', 'c', 0};
或
char str[4];
sprintf(str, "abc");