如果 x 的类型是 char * 而不是 char[N],那么 printf("%s\n", x) 是如何工作的?
Posted
技术标签:
【中文标题】如果 x 的类型是 char * 而不是 char[N],那么 printf("%s\\n", x) 是如何工作的?【英文标题】:How does printf("%s\n", x) work, if x is of type char * instead of char[N]?如果 x 的类型是 char * 而不是 char[N],那么 printf("%s\n", x) 是如何工作的? 【发布时间】:2019-01-11 09:58:57 【问题描述】:一些示例代码:
#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 有什么区别?它们不都是指向字符的地址吗?
非常感谢。
[编辑] 非常感谢您的详细回答!
让我总结一下 func1() 的警告和错误。
char x[10] = "hello";
&x 的类型是 char (*)[10],它是指向数组的指针,而不是指向指针的指针(例如 char **)。因此发出警告。
x 已经是地址(指向字符串),只能取实际内存空间的地址。 &x 不是 x 的地址。 **x in func1(): 试图从非法地址(本例中的“H”值?)读取,因此出现分段错误。
额外的问题是:
使用 &x 的用例是什么(例如 char (*) [N])?
我看到了 x 和 &x、char * / char[N] 与 char (*) [N] 之间的区别,但我无法确定必须使用 char(*) [N] 的情况。 x 和 &x 毕竟是同一个地址。
【问题讨论】:
C 中的 char* 字符串以零结尾。这意味着 printf 语句不知道字符串的长度,而只是打印每个字符,直到找到的第一个零字符。 @Adder,啊,有道理。那为什么func1中的printf会失败呢?char **x
是一个指向指针数组(实际上是你的字符串)的指针。使用它的值是&(*x)
,它就是x
-> printf("func1, %s, %lu\n", x, sizeof(x));
@Hearner:char **x
不是指向指针数组的指针。它是一个指向char
的指针。换句话说,它的类型不包括任何类型的数组。在指向的位置可能有一个指针数组,但这不是char **x
类型的一部分。
sizeof
must be printed using %zu
【参考方案1】:
存在“警告:不兼容的指针类型将
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);
Demo.
【讨论】:
是的! &x 不是 x 的地址!谢谢你的例子。字符 *px = x; px 是:-) 使用 &x 的用例是什么(其中 x 是 char[N] 类型)? @user7586189 当你确实需要一个指向数组的指针时,你会这样做——比如,迭代一个固定大小的数组数组。 但是 x(对于 char[N] 类型,没有 &)也能完成这项工作吗? @user7586189char(*)[N]
是一个指向数组的指针,所以它不会衰减到另一种类型。只有数组衰减为指针。【参考方案2】:
长度信息(即char[N]的N)是否传递给func0?
没有。 func0
的参数是 char *
。这是指向char
的指针,仅此而已。没有与之关联的长度。
但是对于func0(),'char [10]'类型不是转换成'char *'吗?
是的。当x
(类型为char [10]
)用作函数参数时,它会自动转换为指向其第一个参数的指针。所以函数接收到一个指向char
的指针。
如果是这样,printf("%s") 怎么还能打印出“hello”?
传递给printf
的指针必须指向以空字符为结尾的字符串的第一个字符。因此,它接收到的指针指向一个包含'h'
的char
,然后是一个包含'e'
的char
,然后是'l'
,然后是'l'
,然后是'o'
,然后是零。 printf
一个一个地打印字符,直到看到零。
func0中的x和func1中的*x有什么区别?
如果func1
被传递了一个指向char
的指针,那么使用*x
将一个指向char
的指针传递给printf
可能没问题。但是,您拨打func1
的电话是func1(&x)
。这不是将指针传递给指向char
的指针。因为x
是一个由10 个char
组成的数组,所以&x
是一个指向一个由10 个char
组成的数组的指针。它不是指向指针的指针。
数组不是指针。尽管数组通常会自动转换为指针,但当数组是一元 &
的操作数时,不会发生这种自动转换。 (当数组是sizeof
或_Alignof
的操作数,或者当数组是用于初始化数组的字符串字面量时,也不会发生这种情况。)
要从数组x
中生成指向char
的指针,您必须将指针的地址指向其第一个字符。在简单的表达式中无法做到这一点,因为x
是数组,而不是指针。您可以使用char *y = x;
创建指向其第一个字符的指针,然后您可以将&y
传递给func1
。
注意
不要使用%lu
打印来自sizeof
的尺寸。使用%zu
。 z
修饰符专门用于尺寸类型size_t
。
【讨论】:
是的,应该使用 "%zu":-) 使用 &x 的用例是什么(其中 x 是 char[N] 类型)?【参考方案3】:对于标题中的问题,print("%s\n", x)
不关心x
是指针还是数组,只要它包含以零结尾的字符串即可。
接下来,只有数组的最高维度可以透明地强制转换为指针,这就是警告的内容。
最后,&x
指向数组的零元素,因此当您在func1
中取消引用它时,数组的前几个元素(甚至可能是其中的八个,所以最右边的两个甚至没有初始化)被重新解释作为指向 char 的指针,几乎可以肯定地指向无处。段错误就是这样产生的。
【讨论】:
以上是关于如果 x 的类型是 char * 而不是 char[N],那么 printf("%s\n", x) 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章
可以/为啥在返回类型中使用 char * 而不是 const char * 会导致崩溃?