printf
-
- 定义
-
-
- printf() 的正常输出格式有四种:
-
- 输出控制符大全
-
-
- 总结:
-
- 知识点习题
在c语言中,有三个函数可以用来在显示器上输出数据,它们分别是:
- puts():只能输出字符串,并且输出结束后会自动换行。
- putchar():只能输出单个字符。(char 类型)
- printf():可以输出各种类型的数据。
其中 printf() 是最灵活,最复杂,最常用的输出函数,其余两个函数都可以被 printf() 所替代!
printf是格式化输出可以自己定义输出的格式;printf("%d\n",a),其中" "之间的是格式说明串。% 后的一个或两个字符是格式说明符,用它来控制输出变量值的形式,
printf可以输入以上两种格式:
- 字符说明符%c 同于putchar;
- 字符串说明符%s 同于puts;
首先,如果在程序中要使用 printf() 那么就必须要包含头文件 stdio.h。
定义
int _cdecl printf ( const char * format, ... );
- _cdecl是c和c++程序的缺省调用方式
_cdedl调用约定:
- 参数从右到左依次入栈
- 调用者负责清理堆栈
- 参数的数量类型不会导致编译阶段的错误
- 固定参数 format(格式控制字符串),包含了两种类型的对象:普通字符和转换说明。
- 在输出时,普通字符将原样不动地复制到标准输出
- 转换说明并不直接输出而是用于控制 printf 中参数的转换和打印。每个转换说明都由一个百分号字符(%)开始,以转换说明结束,从而说明输出数据的类型、宽度、精度等 。
printf 的格式控制字符串 format 中的转换说明组成如下,其中 [] 中的部分是可选的:
%[flags][width][.precision][length]specifier,即:%[标志][最小宽度][.精度][类型长度]说明符 。(其中,末尾的说明符字符是最重要的组成部分,因为它定义了类型及其相应实参的解释)
- 可变参数(用”…”表示)根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。
- 返回值:如果函数执行成功,则返回 所打印的字符总数(计数针对所有的打印字符,包括空格和不可见的换行字符),如果函数执行失败,则返回一个负数。
函数调用的参数期望至少有1个!!!
printf() 的危险输出:
int main(){
const char * c = "abc";
printf(c);
return 0;
}
代码生成图:
printf() 的正常输出格式有四种:
1)printf(“字符串\n”);
#include
int main(){
printf("hello word!\n");
return 0;
}
其中 \n 表示换行的意思,它是一个转义字符。它的意义:换行(lf),将当前位置移到下一行开头。
printf() 中的双引号和后面的分毫必须是在英文输入法下的,双引号内的字符串并没有要求,可以是英文也可以是中文。
2)printf(“输出控制符”, 输出参数);
#include
int main(){
int i = 10;
printf("%d\n", i);
//%d是输出控制符,d 表示是十进制,后面的 i 是输出参数
return 0;
}
这句话的意思是将变量 i 以十进制输出。
那么现在有一个问题:i 本身就是十进制,为什么还要将 i 以十进制输出呢?
因为程序中虽然写的是 i=10,但是在内存中并不是将 10 这个十进制数存放进去,而是将 10 的二进制代码存放进去了。计算机只能执行二进制 0、1 代码,而 0、1 代码本身并没有什么实际的含义,它可以表示任何类型的数据。所以输出的时候要强调是以哪种进制形式输出。所以就必须要有“输出控制符”,以告诉操作系统应该怎样解读二进制数据。
如果是%x就是以十六进制的形式输出,要是%o就是以八进制的形式输出,大家可以自己试一下。
3) printf(“输出控制符1 输出控制符2…”, 输出参数1,输出参数2, …);
#include
int main(){
int i = 10;
int j = 20;
printf("%d %d", i, j);
return 0;
}
输出控制符 1 对应的是输出参数 1,输出控制符 2 对应的是输出参数 2……编译、链接、执行后我们看一下输出结果:
为什么 10 和 3 之间有一个空格?因为上面 %d 和 %d之间有空格,printf() 中双引号内除了输出控制符外,所有其余的普通字符全部都原样输出。举例如下:
#include
int main(){
int i = 10;
int j = 20;
printf("i = %d j = %d", i, j);
return 0;
}
i = ,空格 和 j = 全都原样输出了。此外需要注意的是:“输出控制符”和“输出参数”无论在“顺序上”还是在“个数上”需一一对应。
4) printf(“输出控制符 非输出控制符”,输出参数);
这个实际上就是上面举的那个例子。这时候会有一个问题:到底什么是“输出控制符”,什么是“非输出控制符”?很简单,凡是以 % 开头的基本上都是输出控制符; 上面的 i = ,空格 和 j = 全都原样输出了,这些就是非输出控制符。
输出控制符大全
常用的输出控制符主要有以下几个:
控制符 | 对应数据类型 | 说明 |
---|---|---|
%d \ %i | int | 输出有符号十进制整数(i 是老式写法) |
%ld \ %lld | long \ long long | 输出长整型数据 |
%o | unsigned int | 输出以八进制整数形式(没有前导 0) |
%u | unsigned int | 输出无符号十进制整数 |
%x \ %x | unsigned int | 输出无符号十六进制整数,x 对应的是 abcdef,x 对应的是 abcdef (没有前导 0x 或者 0x) |
%c | char | 用来输出一个字符 |
%s | char * | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的 但是此时要先定义字符数组或字符指针存储或指向字符串 |
%f \ %lf | double | 用来输出实数,包括单精度和双精度,以小数形式输出 不指定字段宽度,由系统自动指定 整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入 |
%e(%e) | double | 以浮点数指数输出[e-(e-)记数法] |
%g(%g) | double | 浮点数不显无意义的零"0" |
%p | void * | 以16进制形式输出指针 |
%% | 不转换参数 | 不进行转换,输出字符‘%’(百分号)本身 |
下面是对组成格式说明的各项符号加以说明:
- %:表示格式说明的起始符号,不可缺少。
- -:表示左对齐输出,如省略表示右对齐输出。
- 0:有0表示指定空位填0,如省略表示指定空位不填。
- m.n:m指 域宽,即对应的输出项在输出设备上所占的字符数。(如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出);n指 精度。用于说明输出的实型数的小数位数。未指定n时,隐含的精度为n=6位。
- h: 用于将整型的格式字符修正为short型。
%zu
用来输出size_t
类型
%x、%x、%#x、%#x 的区别:
因为调试的时候经常要将内存中的二进制代码全部输出,然后用十六进制显示出来。下面写一个程序看看它们四个有什么区别:
# include
int main(){
int i = 30;
printf("%x\n", i);
printf("%x\n", i);
printf("%#x\n", i);
printf("%#x\n", i);
return 0;
}
以下是代码生成结果(vs-2013):
从输出结果可以看出:如果是小写的x,输出的字母就是小写的;如果是大写的x,输出的字母就是大写的;如果加一个#,就是以标准的十六进制形式输出。
但在我们使用过程中,最好是加一个 # ,否则如果输出的十六进制数正好没有字母的话会误认为是一个十进制数呢!总之,不加 # 容易造成误解。但是如果输出 0x1e 或 0x1e,那么人家一看就知道是十六进制。而且%#x和%#x中,笔者觉得大写的比较好,因为大写是绝对标准的十六进制写法。,那么人家一看就知道是十六进制。
如何输出 %d、\ 和双引号:
printf 中有输出控制符%d,转义字符前面有反斜杠\,还有双引号。那么各位有没有想过这样一个问题:怎样将这三个符号通过 printf 输出到屏幕上呢?
要输出%d只需在前面再加上一个%,要输出\只需在前面再加上一个\,要输出双引号也只需在前面加上一个\即可。程序如下:
# include
int main(){
printf("%%d\n");
printf("\\\n");
printf("\"\"\n");
return 0;
}
代码生成结果如下:
打印较长字符串:
有时printf 语句会很长,以至于不能在一行被放下,如果我们必须分割一个字符串,有以下三种方式可以选择。需要注意的是,我们可以在字符串中使用 “\n” 换行符来表示换行字符,但是在字符串中不能通过回车键来产生实际的换行字符。
示例代码:
#include
int main() {
//方式一:使用多个printf语句
printf("here's one way to print a ");
printf("long string.\n");
//方式二:使用反斜杠 "\" 加回车的组合来进行分割,注意下一行要从最左侧开始,否则缩进会成为该字符串的一部分
printf("here's another way to print a \
long string.\n");
//方式三:采用字符串连接的方法,中间不能有逗号,可以是空格或者回车
printf("here's the newest way to print a "
"long string.\n");
return 0;
}
//程序运行结果:
here's one way to print a long string.
here's another way to print a long string.
here's the newest way to print a long string.
总结:
printf 是c语言中非常重要的一个函数。经过上面的学习我们发现,其实它并不难。只要多编程多练习,很快就能掌握。
输出控制符中,%d、%f、%s、%c 是最常用的,它们分别是输出整数、实数、字符串和字符的控制符。%.mf 虽然用得不多,但一定要重视。
笔者也是c语言入门不久,未能达到大成。如若那块有问题,欢迎留言纠正,谢谢!
知识点习题
int main(){
int a;
float b,c;
scanf("-?o",&a,&b,&c);
printf("\na=%d,b=%d,c=%f\n",a,b,c);
}
若运行时从键盘上输入9876543210l,则上面程序在gcc编译器下的输出结果是
a. a=98,b=765,c=4321.000000
b. a=98,b=0,c=0.000000
c. a=98,b=765.000000,c=4321.000000
d. a=98,b=765.0,c=4321.0
正确答案:
b
答案解析:
a的输出应该是没有疑问的。问题是b读到的是765.0(实数),但是以%d格式输出就出源错了。
必须要以%f格式输出b的值。如果要以整知数形式输出,可以用 %.0f 输出b的值的。
b的格式改正道后,c的输出也正常了。
#include
int main()
{
int a;
float b, c;
scanf("-?o", &a, &b, &c);
printf("1: a=%d,c=%f\n", a, c);
printf("2: a=%d,b=%d,c=%f\n", a, b, c);
printf("3: a=%d,b=%.0f,c=%f\n", a, b, c);
return 0;
}
代码生成图:
问题出现在了printf("\na=%d,b=%d,c=%f %d\n", a, b, c);
中,然后和小伙伴一起想了很长时间,查了相关资料得到了以下重重结论:
scanf函数执行的时候没有任何问题,符合正常理解:int类型数a=98,float类型数b=765.000 000、c=4321.000 000。那么问题就出现在了printf函数。
printf函数执行的时候,会先把这三个数字压入栈里,然后再执行打印。压入栈的时候按照数据本身的长度来,压栈顺序为c–>b–>a,c、b是8个字节(%f输出又如何呢?由于c语言中的默认参数提升规则,%f输出的不论是float还是double都会被提升到双精度进行输出,并不会有精度丢失,故printf自动转化为double),a是4个字节。然后再执行打印。打印的时候按照用户指定的格式来出栈。首先打印a,a打印正常。然后又打印4个字节长度的b,在栈里面由于b长度是八个字节,并且b目前是64位的表示方式,数据的后面全是0(float 变double),电脑是小端存储方式(低地址存低位,高地址村高位),0存储在距离a近的地方。打印b的时候,打印的4个字节都是0。然后再打印c,c用正常的方式打印,会一下子读取8个字节,正好,读出来的八个字节前面四个字节全是0,自己可以算一下,实在太小了,因此为0。
栈底 栈顶
高字节 低字节
4321 0000 765 0000 98
4字节 4字节 4字节 4字节 4字节
打印c 打印b 打印a
附:浮点数(单精度的float和双精度的double)在内存中以二进制的科学计数法表示,表达式为n = 2^e * f;其中e为阶码(采用移位存储),f为尾数。
float和double都由符号位、阶码、尾数三部分组成,float存储时使用4个字节,double存储时使用8个字节。各部分占用位宽如下所示:
符号位 阶码 尾数 长度
float 1 8 23 32
double 1 11 52 64
- 下列代码的输出是?(注:print已经声明过)
main(){
char str[]="geneius";
print (str);
}
print(char *s){
if(*s){
print( s);
printf("%c",*s);
}
}
a. suiene
b. neius
c. run-time error
d. suieneg
正确答案
a
答案解析
既然错了那就认认真真一遍一遍写出来,看看是哪里错了:
1、*s为g,执行print(e),printf(e)被压入倒数第1层,应该输出e;
2、*s为e,执行print(n),printf(n)被压入倒数第2层,应该输出n;
3、*s为n,执行print(e),printf (e) 被压入倒数第3层,应该输出e;
4、*s为e,执行print(i),printf (i) 被压入倒数第4层,应该输出i;
5、*s为i,执行print(u),printf (u) 被压入倒数第5层,应该输出u;
6、*s为u,执行print(s),printf (s) 被压入倒数第6层,应该输出s;
7、*s为s,执行print(\0),printf (\0) 被压入倒数第7层,应该输出空;
8、开始跳出print,从上向下得到压入栈中的printf,得到最后结果“ suiene”
- 有以下程序
#include
#include
using namespace std;
int main(){
int m=0123, n = 123;
printf("%o %o\n", m, n);
return 0;
}
程序运行后的输出结果是()
a. 0123 0173
b. 0123 173
c. 123 173
d. 173 173
正确答案: c
答案解析:
程序中定义了两个整型变量,一个用八进制表示,一个用十进制表示,输出语向中要求m,n都为八进制输出,显然m原样输出为123,n的值转换为八进制输出(123的八进制为173),故输出173。