C 语言的 printf() 函数
概述
printf() 的作用: Print formatted output to the standard output stream.
printf() 的函数原型:
int printf( const char *format, argument1, argument2, ... );
printf() 函数的基本格式:
printf("格式字符串", 待打印项1, 待打印项2, ...);
待打印项1, 待打印项2 等都是要打印的项, 可以是变量, 常量或者在打印之前可以先进行求值的表达式, 然后打印这个表达式的值.
格式字符串中包含两种不同的信息:
- 实际要打印的字符.
- 转换说明.
示意图 1:
示意图 2:
向 printf 或 其他没有在函数原型中指明形参类型的函数, 传递实参时, short 类型实参被转换为 int, float 类型实参被转换为 double.
printf() 格式控制符的完整形式
printf() 格式控制符的完整形式如下:
%[flag][width][.precision]type
[ ] 表示此处的内容可有可无,是可以省略的。
type 表示输出类型,即基本的转换说明,或称为转换字符。
width 表示最小输出宽度。
flag 表示标记。
precision 表示精度。
除了 type 之外统称为修饰符。
转换说明
整型
int
int 类型, 即基本整型, 通常只有十进制, 转换说明为:
%d
short int
short int 简称为 short, 即短整型. 转换说明要加前缀 h, 即:
%hd
long int
long int 简称为 long, 即长整型. 转换说明要加前缀 l, 即:
%ld
long long int
long long int 简称为 long long, 即长长整型, 是 C99 新增, C90 不支持. 转换说明要加前缀 ll, 即:
%lld
unsigned int
unsigned int 简称 unsigned, 即无符号基本整型.
有三种进制, 即十进制, 八进制, 十六进制.
以八进制和十六进制输出时, 会将符号位视为数值位, 故以八进制或十六进制形式输出整数时只能用于无符号整数.
printf() 不支持以八进制和十六进制形式输出有符号数, 也没有对应的转换说明符.
十进制:
%u
十进制没有前缀, 若加了 # 则结果未定义.
八进制:
显示前缀:%#o(得到前缀 0) 不显示前缀:%o
十六进制:
显示前缀:%#x(得到前缀 0x),%#X(得到前缀 0X) 不显示前缀:%x,%X 使用 x 则十六进制数用 1-9 和 a-f 表示. 使用 X 则十六进制数用 1-9 和 A-F 表示.
程序示例:
#include<stdio.h> int main(void) { unsigned int x = 100; printf("dec = %u; octal = %o; hex = %x\n", x, x, x); printf("dec = %u; octal = %o; hex = %X\n", x, x, x); printf("dec = %u; octal = %#o; hex = %#x\n", x, x, x); printf("dec = %u; octal = %#o; hex = %#X\n", x, x, x); return 0; }
结果:
dec = 100; octal = 144; hex = 64 dec = 100; octal = 144; hex = 64 dec = 100; octal = 0144; hex = 0x64 dec = 100; octal = 0144; hex = 0X64
unsigned short int
unsigned short int 简称 unsigned short, 即无符号短整型, 分三种进制进行打印:
十进制:
%hu
八进制:
显示前缀: %#ho 不显示前缀: %ho
十六进制:
显示前缀: %#hx 或 %#hX 不显示前缀: %hx 或 %hX
unsigned long int
unsigned long int 简称为 unsigned long, 即无符号长整形.
十进制:
%lu
八进制:
显示前缀: %#lo 不显示前缀: %lo
十六进制:
显示前缀: %#lx 或 %#lX 不显示前缀: %lx 或 %lX
unsigned long long int
unsigned long long int 简称为 unsigned long long, 即无符号长长整型.
十进制:
%llu
八进制:
显示前缀: %#llo 不显示前缀: %llo
十六进制:
显示前缀: %#llx 或 %#llX 不显示前缀: %llx 或 %llX
规律:
short, long, long long 就是在 int 的 d 前面加前缀 h, l, ll. unsigned short, unsigned long, unsigned long long 就是在 unsigned 的 u, o, x/X 前面加前缀 h, l, ll.
转换说明中的字母尽量都用小写,除了表示十六进制的 X(经测试,小写都不会出错,大写可能出错)。
此类前缀称为修饰符.
有符号类型只能以十进制输出 (虽然有符号的正数以十六进制和八进制输出也不会出错, 但只是刚好符号位为 0 而已).
无符号类型可以三种进制 (十进制, 八进制, 十六进制) 输出. 要显示八进制前缀 0 和十六进制前缀 0x 或 oX 时在转换说明的 % 后面加 #.
布尔型
转换说明:
%u
字符型
转换说明:
%c,%d,%o,%x
程序示例:
#include <stdio.h> int main(void) { char c = 'a'; printf("%c\t%d\t%o\t%x\n", c, c, c, c); // a 97 141 61 return 0; }
地址
转换说明:
%p
指针之差
两个指针差值的类型为 ptrdiff_t,这是 C99 引入的类型。这是一个底层有符号整数类型。
%td
sizeof 和 strlen() 的返回值类型
这个类型为 size_t,这是 C99 引入的类型。这是一个底层无符号整数类型。
%zd
浮点型
float 和 double
十进制:
%f
指数计数法:
%e %E
十六进制:
%a %A
long double
十进制:
%Lf
指数计数法:
%Le
十六进制:
%La
给未在函数原型中显式说明参数类型的函数(如 printf())传递参数时,C 编译器会把 float 类型的值自动转换为 double 类型。
%f, %e, %E, %Lf, %Le 都是默认小数点后输出 6 位。其中,%e, %E, %Le 都是默认小数点前输出 1 位。全部都可以四舍五入。
程序示例:
#include<stdio.h> int main(void) { float a = 123.123456789; long double b = 123.123456789L; printf("%f\n", a); printf("%e\n", a); printf("%Lf\n", a); printf("%Le\n", a); return 0; }
执行结果:
123.123459 1.231235e+02 123.123459 1.231235e+02
%g
根据值的不同, 自动选择 %f 或者 %e 来打印浮点数, %e 格式用于指数小于 -4 或者大于或等于精度时.
%G
根据值的不同, 自动选择 %f 或者 %E 来打印浮点数, %e 格式用于指数小于 -4 或者大于或等于精度时.
修饰符
以上各个百分号后面的字母叫做 转换字符
. 在 转换字符
和 百分号
之间还可以有 修饰符
. 修饰符可以修饰基本的转换说明.
转换说明即由三部分组成: 百分号, 修饰符, 转换字符 .
数字
表示宽度
转换说明中, 小数点前面的数字可以表示待打印项的最小字段宽度, 在指定宽度范围内, 内容右对齐.
如果字段宽度不能容纳待打印的内容, 系统会自动使用刚好合适的宽度.
就是%[flag][width][.precision]type
当中的 width 部分。
程序示例 1:
#include<stdio.h> int main(void) { printf("**********\n"); printf("%10s\n", "hello"); return 0; }
结果:
********** hello
程序示例 2:
#include<stdio.h> int main(void) { printf("**********\n"); printf("%10d\n", 30); return 0; }
结果:
********** 30
程序示例 3:
#include<stdio.h> int main(void) { printf("**********\n"); printf("%10s\n", "hello world!"); return 0; }
结果:
********** hello world!
表示精度
转换说明中, 小数点后面的数字还可以表示浮点数的精度, 即浮点数的小数点后面有几个数字, 可以四舍五入.
就是%[flag][width][.precision]type
当中的 precision 部分。
程序示例:
#include<stdio.h> #include<string.h> int main(void) { float a = 1235.56789; printf("%e\n", a); // %e,%f,%Lf默认小数点后面6个数字,可以进行四舍五入 printf("%.4e\n", a); // 明确指定小数点后面4个数字,可以进行四舍五入 printf("%.4f\n", a); // 明确指定小数点后面4个数字,可以进行四舍五入 return 0; }
结果:
1.235568e+03 1.2356e+03 1235.5679
这种精度表示方式适用于 %e,%E 和 %f.
小数点前面的数字表示字段宽度, 小数点后面的数字表示输出的浮点数的小数点后面的位数.
可以看出, 这里发生了四舍五入.
如果转换说明中, 只是用了小数点, 则说明输出的数字没有小数部分, 即没有小数点以及小数点后面的数字. 也就是说 %.f
和 %.0f
两者效果相同.
在去掉小数部分时依然会发生四舍五入.
程序示例:
#include<stdio.h> #include<string.h> int main(void) { float a = 12345.88888; printf("%.f\n", a); // 只有小数点 printf("%.0f\n", a); return 0; }
结果:
12346 12346
可以看出这里发生了四舍五入.
表示待打印字符的最多个数
在转换说明 %s
中, 在小数点后面加数字, 可以表示待打印的字符的最多个数.
程序示例:
#include<stdio.h> #include<string.h> int main(void) { char name[20] = "helloworld"; printf("%s\n", name); // 直接输出整个字符串 printf("%.5s\n", name); // 最多输出5个字符 printf("********************\n"); printf("%10.6s\n", name); // 输出的字符串最多占用10个位置,最多输出6个字符,默认右对齐 printf("%-10.5s\n", name); // 输出的字符最多占用10个位置,最多输出5个字符,左对齐 return 0; }
结果:
helloworld hello ******************** hellow hello
表示整数的最少位数
当 %d
转换说明中, 小数点后面有数字时, 该数字表示待输出的整数最小占用的位数, 如果指定的位数小于数字的真实位数, 则忽视这个位数的要求而直接打印数字, 如果指定的位数大于数字的真实位数, 则数字靠右打印, 左侧用数字 0 补齐要求的位数. 不论哪种情况待打印的数字都是完整输出的.
程序示例:
#include<stdio.h> #include<string.h> int main(void) { int a = 12345; // 最小打印四位数字,原数字超过了四位,则直接忽视这个4,打印原始的数字 printf("%.4d\n", a); // 最小打印六位数字,原数字为五位,则打印原始的数字,原始数字靠右,左侧用0补齐六位 printf("%.6d\n", a); return 0; }
结果:
12345 012345
程序示例:
#include<stdio.h> #include<string.h> int main(void) { int a = 12345; printf("**********\n"); printf("%10.7d\n", a); // 指定字段宽度为 10, 至少输出 7 个数字, 不够的用 0 不足位数 return 0; }
结果:
********** 0012345
标记
标记有五种: -, +, 空格, #, 0.
可以不使用或者使用多个标记.
就是%[flag][width][.precision]type
当中的 flag 部分。
- 标记
-
可以使得在指定的字段范围内, 待打印项左对齐.
程序示例:
#include<stdio.h> int main(void) { int a = 123; printf("**********\n"); printf("%-10d\n", a); return 0; }
结果:
********** 123
+ 标记
有符号值若为正, 则在前面显示 +. 若为负, 则在前面显示 -.
程序示例:
#include<stdio.h> int main(void) { int a = 123; int b = -123; printf("%+d\n", a); printf("%+d\n", b); return 0; }
结果:
+123 -123
空格
有符号值若为正, 则在前面显示空格, 不显示任何符号, 若为负, 则在前面显示减号.
如果在转换说明的修饰符中, 正号 + 和空格同时存在, 则正号起作用而空格不起作用.
程序示例:
#include<stdio.h> int main(void) { int a = 123; int b = -123; printf("% d\n", a); printf("%+ d\n", a); // 正号 + 和空格同时存在, 则正号起作用而空格不起作用 printf("% d\n", b); printf("%+ d\n", b); // 正号 + 和空格同时存在, 则正号起作用而空格不起作用 return 0; }
结果:
123 +123 -123 -123
#
标记
对于 %o, 输出将以 0 开始.
对于 %x 和 %X, 输出将以 0x 和 0X 开头.
对于所有的浮点数, 即使没有小数部分也会打印一个小数点.
程序示例:
#include<stdio.h> #include<string.h> int main(void) { float a = 123e2; printf("%.0f\n", a); printf("%#.0f\n", a); return 0; }
结果:
12300 12300.
对于 %g 和 %G 格式, #
防止结果后面的 0 被删除.
0 标记
对于数值格式, 用前导 0 代替空格填充字段宽度.
对于整数格式, 如果出现 - 标记或者指定最少输出的数字个数, 则忽略 0 这个标记.
程序示例 1:
#include<stdio.h> int main(void) { int a = 88; printf("**********\n"); printf("%10d\n", a); printf("%010d\n", a); // 用前导 0 代替空格填充字段宽度 printf("%-010d\n", a); // 出现 - 标记, 标记 0 不起作用 return 0; }
结果:
********** 88 0000000088 88
程序示例 2:
#include<stdio.h> int main(void) { float a = 1.2345; printf("**********\n"); printf("%10.3f\n", a); printf("%010.3f\n", a); // 对于浮点数,指定了精度,但标记 0 依然起作用,只有整数里面的标记 0 可能被覆盖不起作用 return 0; }
结果:
********** 1.235 000001.235
程序示例 3:
#include<stdio.h> int main(void) { int a = 12345; printf("**********\n"); printf("%10d\n", a); // 指定字段宽度为10 printf("%10.3d\n", a); // 指定最少输出3位,实际为5位,因此3被忽视,直接输出原来的数字 printf("%010d\n", a); // 不指定整数最少输出的数字个数,标记 0 起作用 printf("%010.3d\n", a); // 指定整数最少输出的数字个数,标记 0 不起作用 return 0; }
结果:
********** 12345 12345 0000012345 12345
转换说明的意义
数据存储在计算机中是以二进制的数字形式存储的, 但是用 printf 将其显示在屏幕上时显示的是字符. 比如在屏幕上显示 42 是显示字符 4 和字符 2.
转化说明就负责这一转换过程. 转换说明就是一种翻译说明. 转换说明把以二进制格式存储在计算机中的值转换成一系列字符(字符串)以便于显示。转换说明是翻译说明。
%%
可以使用 %% 打印一个百分号 %
程序示例:
#include<stdio.h> #include<string.h> int main(void) { printf("Only %d%% students are good.", 30); return 0; }
结果:
Only 30% students are good.
常见错误
1.
用 %d 打印浮点数时,不会将浮点数转换为整型。
程序示例:
#include<stdio.h> int main(void) { float a = 10.1111115; printf("%d\n", a); return 0; }
结果:
-1610612736
各个系统结果可能不同。
刷新输出
printf() 语句先把输出发送到一个叫做缓冲区的中间存储区域,然后缓冲区中的内容再不断地被发送到屏幕上。
C 标准明确规定了何时把缓冲区中的内容发送到屏幕:当缓冲区满,遇到换行符或者需要输入时。
从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区或清空缓冲区。
C 标准明确规定了何时向屏幕发送缓冲区的内容, 即刷新缓冲区: 缓冲区满了或遇到换行符或需要输入。
缓冲区分为输入缓冲区和输出缓冲区。输入分为有缓冲输入和无缓冲输入,输出分为有缓冲输出和无缓冲输出。
如果输入的内容能立即被程序使用,则为无缓冲输入。如果输入的内容需要在刷新了缓冲区后才能被程序利用,则为有缓冲输入。C 标准为有缓冲输入。
某些交互式程序要求输入必须是无缓冲的,比如打游戏。
有缓冲输入/输出分为行缓冲和块缓冲,块缓冲也叫完全缓冲。
完全缓冲是指当缓冲区被填满时才刷新缓冲区,通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小为 512 字节和 4096 字节。
行缓冲是指在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下 Enter 键之后才刷新缓冲区。
printf 函数的返回值
returns the number of characters printed, or a negative value if an error occurs.
printf()
函数返回它打印的字符的个数.
如果输出有误, 则 printf()
返回一个负值.
程序示例:
#include<stdio.h> int main(void) { printf("%d\n", printf("Hello\n")); return 0; }
结果:
Hello 6
显然, 需要求 printf("Hello\n")
表达式的返回值, 需要先计算该表达式, 效果是打印出一句话, 即 Hello
.
此返回值包括了所有的字符, 尤其是空格和转义字符如 \n
等.
* 修饰符的用法
输出时可以不预先指定字段的宽度, 可以通过程序来指定.
可以用 * 修饰符来替换字段宽度.
此时需要另一个参数来告诉函数, 这个字段宽度应该是多少.
即, 如果转换说明是 %*d
, 则参数列表中应该包含 *
和 d
对应的值.
该技巧也可用于浮点值指定精度和字段宽度.
程序示例:
#include<stdio.h> #define NUMBER 123 int main(void) { printf("Please enter field width: "); int width = 0; scanf("%d", &width); printf("This integer is %*d\n", width, NUMBER); return 0; }
结果:
Please enter field width: 6 This integer is 123
程序示例:
#include<stdio.h> #define NUMBER 123.456789 int main(void) { printf("Please enter field width: "); int width = 0; scanf("%d", &width); printf("Please enter precision: "); int precision = 0; scanf("%d", &precision); printf("This number is %*.*f.\n", width,precision,NUMBER); return 0; }
结果:
Please enter field width: 14 Please enter precision: 3 This number is 123.457.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术