一些示例代码:
#include <stdio.h>
void func0(char *x)
{
printf("func0, %s, %zu\n", x, sizeof(x));
}
//for comparison with func0
void func1(char **x)
{
printf("func1, %s, %zu\n", *x, sizeof(*x));
}
int main()
{
char x[10] = "hello";
printf("%s, %zu, %zu, %zu\n", x, sizeof(x), sizeof(*x), sizeof("hello"));
func0(x);
func1(&x);
return 0;
}
对于func1()(与func0进行比较),有一个“警告:不兼容的指针类型,将'char(*)[10]'传递给'char **'类型的参数”和“分段错误”错误。
但是对于func0(),类型'char [10]'是否转换为'char *'?如果是这样,printf(“%s”)仍能打印出“ hello”吗?如func0()的输出所示:
hello, 10, 1, 6
func0, hello, 8
长度信息(即char [N]的N)是否传递给func0?
func0中的x和func1中的* x有什么区别?他们俩都不都是指向字符的地址吗?
非常感谢您。
[EDIT]非常感谢您提供详细的答案!
让我总结一下func1()的警告和错误。
char x[10] = "hello";
&x的类型为char(*)[10],它是指向数组的指针,而不是指向指针的指针(例如char **)。因此,警告。
x已经是地址(指向字符串),并且您只能使用实际内存空间的地址。 &x不是x的地址。 ** x在func1()中:尝试从非法地址(在此示例中为“ H”的值?)进行读取,因此出现了分段错误。
另一个问题是:
使用&x的用例是什么(例如char(*)[N])?
我看到x和&x之间的区别,char * / char [N] vs. char(*)[N],但是我无法弄清楚必须使用char(*)[N]的情况。 x和&x毕竟是相同的地址。
答案 0 :(得分:4)
有一个“警告:将
char (*)[10]
传递到类型char **
的参数的不兼容指针类型”和“分段错误”错误。
是的,&x
表达式产生一个指向十个字符的数组的指针,因此发出警告。如果您想将指针传递给指针,请创建一个指针,然后获取它的指针:
char *px = x; // alternatively you could write &x[0]
func1(&px);
但是对于
func0()
,不是将类型char [10]
转换为char *
吗?
是的,在进行函数调用时,字符数组会“衰减”到字符指针。
printf("%s")
仍然能够打印出"hello"
之所以可行,是因为x
包含一个以空字符结尾的字符序列。因此,printf
在处理%s
时不需要知道数组的大小:在'\0'
停止就足够了。
本质上,%s
不需要知道数组的大小,它需要知道字符串的长度,就像strlen
“知道”它的方式一样:
printf("func0, %s, %lu\n", x, strlen(x));
// Prints hello 5
我看到了
x
(char
/char[N]
)和&x
之间的区别,但是我无法弄清楚您必须使用char(*) [N]
的情况
这是一个小例子:
void print_many(char (*rows)[10], size_t count) {
for (size_t i = 0 ; i != count ; i++) {
printf("%zu: %s\n", i+1, rows[i]);
}
}
您这样称呼它:
char rows[10][] = {"quick", "brown", "fox"};
print_many(rows, 3);
答案 1 :(得分:1)
长度信息(即char [N]的N)是否传递给func0?
不。 func0
的参数为char *
。这是指向char
的指针,仅此而已。没有相关的长度。
但是对于func0(),类型'char [10]'是否转换为'char *'?
是的。当类型为x
的{{1}}用作函数参数时,它将自动转换为指向其第一个参数的指针。因此,该函数将接收到指向char [10]
的指针。
如果是这样,printf(“%s”)仍能打印出“ hello”吗?
传递给char
的指针必须指向字符串的第一个字符,该字符串的结尾用空字符标记。因此,它接收的指针指向包含printf
的{{1}},此后是{'1}}包含'char
,然后是'h'
,然后是{{1 }},然后是char
,然后是零。 e'
逐个打印字符,直到看到零为止。
func0中的x和func1中的* x有什么区别?
如果向'l'
传递了指向'l'
的指针,那么可以使用'o'
将指向printf
的指针传递给func1
。但是,您对char
的呼叫是*x
。这没有传递指向char
的指针的指针。由于printf
是10 func1
的数组,因此func1(&x)
是指向10 char
的数组的指针。它不是指向指针的指针。
数组不是指针。尽管数组通常会自动转换为指针,但是当数组是一元x
的操作数时,则不会发生这种自动转换。 (当数组是char
或&x
的操作数,或者数组是用于初始化数组的字符串文字时,也不会发生这种情况。)
要生成指向数组char
中指向&
的指针的指针,您必须获取指向其第一个字符的指针的地址。无法通过简单的表达式来执行此操作,因为sizeof
是数组,而不是指针。您可以使用_Alignof
创建指向第一个字符的指针,然后可以将char
传递给x
。
请勿使用x
打印来自char *y = x;
的尺寸。使用&y
。 func1
修饰符专门用于尺寸类型sizeof
。
答案 2 :(得分:0)
对于标题中的问题,print("%s\n", x)
并不关心x
是指针还是数组,只要它包含以零结尾的字符串即可。
接下来,只有数组的最高维可以透明地强制为指针,这就是警告所在。
最后,&x
指向数组的零个元素,因此在func1
中取消引用它时,数组的前几个元素(甚至可能是八个),因此最右边的两个元素甚至不初始化)重新解释为指向char的指针,几乎可以肯定指向无处。这就是段错误的产生。