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. 转换说明.

示意图 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.
posted @   有空  阅读(108)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示

目录导航