我试图学习指针,我写了下面的代码来打印指针的值:
#include <stdio.h>
int main(void) {
char *p = "abc";
printf("%c",*p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
一个
但是,如果我将上面的代码更改为:
#include <stdio.h>
int main(void) {
char *p = "abc";
printf(p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我得到输出:
ABC
我不明白以下两件事:
为什么printf在第二种情况下不需要格式说明符?是否printf(pointer_name)足以打印指针的值?
根据我的理解(这很少),*p指向包含的连续内存块abc.我预计两个输出都是相同的,即
ABC
由于印刷方式不同,是不同的产出?
编辑1
此外,以下代码会产生运行时错误.为什么这样?
#include <stdio.h>
int main(void) {
char *p = "abc";
printf(*p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Som*_*ude 15
对于您的第一个问题,printf函数(和系列)将字符串作为第一个参数(即a const char *).该字符串可以包含该printf函数将替换为相应参数的格式代码.文本的其余部分按原样逐字打印.这就是当你p作为第一个参数传递时发生的事情.
请注意,使用printf这种方式是非常不推荐的,特别是如果字符串包含来自用户的输入.如果用户在字符串中添加格式代码,并且您没有提供正确的参数,那么您将具有未定义的行为.它甚至可能导致安全漏洞.
对于第二个问题,变量p指向一些内存.表达式*p取消引用指针,为您提供单个字符,即p实际指向的字符,即p[0].
想象p这样:
+---+ +-----+-----+-----+------+ | p | ---> | 'a' | 'b' | 'c' | '\0' | +---+ +-----+-----+-----+------+
变量p并不真正指向"字符串",它只指向内存中的某个单独位置,即字符串中的第一个字符"abc".使用它的函数p将内存视为一系列字符.
此外,常量字符串文字实际上存储为字符串中字符数的(只读)数组,以及字符串终止符的一个数组.
此外,为了帮助你理解为什么*p是一样的p[0],你需要知道的是,对于任何指针或数组 p和有效的索引i,表达式p[i]等于*(p + i).要获得第一个字符,你有索引0,这意味着你p[0]应该等于*(p + 0).对任何东西添加零是一个无操作,所以*(p + 0)是*(p)相同的*p.因此p[0]等于*p.
关于你的编辑(你在哪里printf(*p)),因为*p返回由p(即p[0])指向的第一个"元素"的值,你传递一个字符作为指向格式字符串的指针.这将导致编译器将其转换成其指向任何地址都有单个字符值的指针(它不字符转换为指针,以字符).此地址不是一个非常有效的地址(在ASCII字母表 'a'中的值97是程序将查找要打印的字符串的地址),并且您将具有未定义的行为.
p 是格式字符串.
char *p = "abc";
printf(p);
Run Code Online (Sandbox Code Playgroud)
是相同的
print("abc");
Run Code Online (Sandbox Code Playgroud)
这样做非常糟糕,因为你不知道变量将包含什么,如果它包含格式说明符,调用printf可能会做很糟糕的事情.
第一种情况(with "%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 */
Run Code Online (Sandbox Code Playgroud)