嗨,我有一个简单的问题
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 **.
或者,等效于%dis 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 书(在这里搜索,我相信你会找到建议)并完成它。