如果x的类型为char *而不是char [N],则printf(“%s \ n”,x)如何工作?

时间:2018-08-03 13:30:00

标签: c arrays char printf

一些示例代码:

#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毕竟是相同的地址。

3 个答案:

答案 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
  

我看到了xchar / 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.

答案 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;的尺寸。使用&yfunc1修饰符专门用于尺寸类型sizeof

答案 2 :(得分:0)

对于标题中的问题,print("%s\n", x)并不关心x是指针还是数组,只要它包含以零结尾的字符串即可。

接下来,只有数组的最高维可以透明地强制为指针,这就是警告所在。

最后,&x指向数组的零个元素,因此在func1中取消引用它时,数组的前几个元素(甚至可能是八个),因此最右边的两个元素甚至不初始化)重新解释为指向char的指针,几乎可以肯定指向无处。这就是段错误的产生。