嗨,我有一个简单的问题
char *a="abc";
printf("%s\n",a);
int *b;
b=1;
printf("%d\n",b);
为什么第一个有效,第二个无效?我觉得第一个应该是
char *a="abc";
printf("%s\n",*a);
我认为 a 存储“abc”的地址。那么为什么当我打印 a 时它显示 abc 呢?我想我应该打印 *a 来获得它的价值。
谢谢
英
为什么第一个有效,第二个无效?
因为首先,您不是要求它打印一个字符,而是要求它打印一个以空字符结尾的字符数组作为字符串。
这里的困惑是您将字符串视为“本机类型”,其方式与整数和字符相同。C 不能那样工作;字符串只是指向以空字节结尾的一堆字符的指针。
如果您真的想将字符串视为本机类型(请记住它们实际上不是),请这样想:字符串的类型是char *
,而不是char
。因此,printf("%s\n", a);
之所以有效,是因为您要传递 achar *
以匹配格式说明符,指示char *
. 要获得与第二个示例相同的问题,您需要传递一个指向字符串的指针,即 a char **
.
或者,等效于%d
is not %s
, but %c
,它打印单个字符。要使用它,您必须向它传递一个字符。printf("%c\n", a)
将遇到与 相同的问题printf("%d\n", b)
。
从您的评论中:
我认为 a 存储“abc”的地址。那么为什么当我打印 a 时它显示 abc 呢?我想我应该打印 *a 来获得它的价值。
这就是将字符串视为原生对象的松散想法失败的地方。
当你写这个:
char *a = "abc";
发生的情况是,编译器存储了一个由四个字符组成的数组——<code>'a'、'b'
、'c'
和'\0'
——某处,并a
指向第一个字符,即a
. 只要您记得它"abc"
实际上是一个由四个单独字符组成的数组,就可以将其a
视为指向该事物的指针(至少如果您了解数组和指针算术在 C 中的工作原理)。但是如果你忘记了这一点,如果你认为a
它指向一个包含单个对象的单个地址"abc"
,那会让你感到困惑。
引用GNUprintf
手册页(因为 C 标准不可链接):
d, 我
int 参数被转换为有符号十进制表示法……</p>
C
... int 参数被转换为无符号字符,并写入生成的字符...</p>
s
… const char * 参数应该是指向字符类型数组的指针(指向字符串的指针)。数组中的字符最多写入(但不包括)一个终止空字节('\0')……</p>
最后一件事:
您可能想知道当没有“字符串”这样的值时,如何printf("%s", a)
或任何其他函数可以打印或搜索字符串。strchr(a, 'b')
他们使用约定:他们获取一个字符的指针,然后打印或搜索从那里到第一个 null 的每个字符。例如,您可以编写print_string
如下函数:
void print_string(char *string) {
while (*string) {
printf("%c", *string);
++string;
}
}
或者:
void print_string(char *string) {
for (int i=0; string[i]; ++i) {
printf("%c", string[i]);
}
}
无论哪种方式,您都假设char *
是指向字符数组开头的指针,而不仅仅是单个字符,并打印数组中的每个字符,直到您遇到空字符。这是“以空字符结尾的字符串”约定,在整个标准库中都融入了printf
、等函数。strstr
字符串在 C 中并不是真正的“一等公民”。实际上,它们只是实现为以空字符结尾的字符数组,并添加了一些语法糖以使程序员的生活更轻松。这意味着在传递字符串时,您主要是通过char *
变量来执行此操作 - 即指向以空字符结尾的char
.
这种做法也适用于 call printf
。%s
格式与打印字符串的参数相匹配——char *
正如您所见。你可以使用*a
,但您希望将其与 a%c
或整数格式匹配以仅打印指向的单个字符。
由于几个原因,您的第二个示例是错误的。首先,在 C 中没有显式强制转换的情况下进行赋值是不合法的b = 1
——你需要b = (int *)1
. 其次,您试图打印出一个指针,但您将%d
其用作格式字符串。这也是错误的 - 你应该使用%p
这样使用: printf("%p\n", (void *)b);
.
在第二个示例中,您真正想要做的是:
int b = 1;
int *p = &b;
printf("%d\n", *p);
也就是说,创建一个指向整数的指针,然后取消引用它并打印出来。
编者注:你应该得到一本很好的初学者 C 书(在这里搜索,我相信你会找到建议)并完成它。