我试图学习指针,并编写了以下代码来打印指针的值:
#include <stdio.h>
int main(void) {
char *p = "abc";
printf("%c",*p);
return 0;
}
输出结果为:
一
但是,如果我将上面的代码更改为:
#include <stdio.h>
int main(void) {
char *p = "abc";
printf(p);
return 0;
}
我得到了输出:
ABC
我不理解以下两件事:
为什么printf在第二种情况下不需要格式说明符? printf(pointer_name)
是否足以打印指针的值?
根据我的理解(很少),* p指向包含abc
的连续内存块。我预计两个输出都是相同的,即
由于打印方式不同,ABC
是不同的输出?
修改1
此外,以下代码会产生运行时错误。为什么这样?
#include <stdio.h>
int main(void) {
char *p = "abc";
printf(*p);
return 0;
}
答案 0 :(得分:12)
对于您的第一个问题,printf
function (and family)将字符串作为第一个参数(即const char *
)。该字符串可以包含printf
函数将替换为相应参数的格式代码。文本的其余部分按原样逐字打印。这就是当您将p
作为第一个参数传递时发生的事情。
请注意,以这种方式使用printf
是非常不推荐的,特别是如果字符串包含来自用户的输入。如果用户在字符串中添加格式代码,并且您没有提供正确的参数,那么您将具有未定义的行为。它甚至可能导致安全漏洞。
对于第二个问题,变量p
指向一些内存。表达式*p
取消引用指针,为您提供单个字符,即p
实际指向的字符,即p[0]
。
想象p
这样:
+---+ +-----+-----+-----+------+ | p | ---> | 'a' | 'b' | 'c' | '\0' | +---+ +-----+-----+-----+------+
变量p
并不真正指向“字符串”,它只指向内存中的某个位置,即字符串"abc"
中的第一个字符。使用p
的函数将该内存视为一系列字符。
此外,常量字符串文字实际上存储为字符串中字符数的(只读)数组加上字符串终止符的数字。
另外,为了帮助您理解为什么*p
与p[0]
相同,您需要知道任何指针或数组 p
和有效索引{ {1}},表达式i
等于p[i]
。要获取第一个字符,您有索引*(p + i)
,这意味着您有0
,然后应该等于p[0]
。向任何内容添加零都是无操作,因此*(p + 0)
与*(p + 0)
相同,与*(p)
相同。因此*p
等于p[0]
。
关于您的修改(您在*p
处),因为printf(*p)
会返回*p
所指向的第一个“元素”的值(即p
)传递单个字符作为指向格式字符串的指针。这将导致编译器将其转换为指向任何具有该单个字符值的地址的指针(它不会将字符转换为指针到字符)。这个地址不是一个非常有效的地址(在ASCII alphabet p[0]
中有值'a'
,这是程序查找要打印的字符串的地址),你将拥有未定义的行为。
答案 1 :(得分:5)
p
是格式字符串。
char *p = "abc";
printf(p);
与
相同print("abc");
这样做非常糟糕,因为你不知道变量是什么
将包含,如果它包含格式说明符,调用printf
可能会做很糟糕的事情。
第一种情况("%c"
)仅打印第一个字符的原因
是%c
表示一个字节,*p
表示p
指向的(第一个)值。
%s
将打印整个字符串。
char *p = "abc";
printf(p); /* If p is untrusted, bad things will happen, otherwise the string p is written. */
printf("%c", *p); /* print the first byte in the string p */
printf("%s", p); /* print the string p */
答案 2 :(得分:2)
你是误解,确实是在做什么时
char *p = "Hello";
p
指向文字&#34; Hello&#34;的起始地址被储存了。这就是声明指针的方式。但是,之后,你做了
*p
表示取消引用 p
并获取p
点的对象。在上面的例子中,这将产生&#39; H&#39;这应该澄清你的第二个问题。
如果是printf,请尝试
printf("Hello");
这也很好;这回答了你的第一个问题,因为它实际上与你将p
传递给printf时所做的一样。
最后到你的编辑,确实
printf(*p);
以上行不正确,因为printf需要const char *
,并且使用*p
您传递char
- 或者换句话说&#39; H&#39;假设我们的例子。阅读更多解除引用的含义。
答案 3 :(得分:2)
- 为什么printf在第二种情况下不需要格式说明符? printf(pointer_name)是否足以打印指针的值?
醇>
“abc”是您的格式说明符。这就是它打印“abc”的原因。如果字符串包含%
,那么事情会表现得很奇怪,但事实并非如此。
printf("abc"); // Perfectly fine!
- 为什么printf在第二种情况下不需要格式说明符? printf(pointer_name)是否足以打印指针的值?
醇>
%c
是字符转换说明符。它指示printf
仅打印第一个字节。如果要打印字符串,请使用...
printf ("%s", p);
%s
似乎是多余的,但它对于打印控制字符或使用宽度说明符非常有用。
理解这一点的最佳方法是尝试使用abc%def
打印字符串printf
。
答案 4 :(得分:2)
为什么printf在第二种情况下不需要格式说明符? printf(pointer_name)是否足以打印指针的值?
使用您的代码告诉printf使用您的字符串作为格式字符串。这意味着您的代码等同于printf("abc")
。
根据我的理解(很少),* p指向包含abc的连续内存块。我预计两个输出都是相同的
如果您使用%c
,则会打印一个字符,如果您使用%s
,则会得到整个字符串。但是如果你告诉printf使用字符串作为格式字符串,那么它也会这样做。
char *p = "abc";
printf(*p);
此代码崩溃,因为p
的内容,字符'a'
不是指向格式字符串的指针,它甚至不是指针。该代码甚至不应在没有警告的情况下编译。
答案 5 :(得分:1)
%c
格式说明符需要char
类型,并会输出单 char
值。
printf
的第一个参数必须是const char*
(char*
可以隐式转换为const char*
)并指向 start 一个字符串的字符。它在遇到该字符串中的\0
时停止打印。如果该字符串中不存在\0
,则该函数的行为为 undefined 。由于"abc"
不包含任何格式说明符,因此在这种情况下,您不会向printf
传递任何其他参数。