C语言转换说明及其修饰符——格式化输入输出

printf()函数

C语言中的printf函数使用转换说明(Conversion Specifier)来格式化输出。一个完整的格式控制符通常以百分号 % 开头,后跟一个或多个字符来指定输出的类型和格式。

其基本结构可以理解为:%[标志][宽度][.精度][长度修饰符]转换说明符。前四个可选参数统称为转换说明修饰符。

转换说明符

用于指定输出数据类型。可单独使用。

转换说明符含义
d输出有符号十进制整数,i与其等价。单独使用时参数类型应为int
u输出无符号十进制整数。单独使用时参数类型应为unsigned int
f输出十进制单精度/双精度浮点数。默认精确到小数点后六位。单独使用时参数类型应为floatdouble
a输出用p记数法表示的十六进制浮点数。若改为A则将输出内容中的小写字母替换为大写。单独使用时参数类型应为floatdouble
g根据值的不同,自动判断是否使用科学计数法。科学计数法默认在指数小于-4时使用;若指定精度,也会在指数大于或等于精度时使用。若改为G则将输出内容中的e替换为E。
单独使用时参数类型应为floatdouble
e输出科学计数法形式的浮点数。若改为E则将输出内容中的e替换为E。
o输出无符号八进制整数,不含前缀。单独使用时参数类型应为unsigned int,若传入有符号整数则输出其八进制补码。
x输出无符号十六进制整数,不含前缀。若改为X则将输出内容中的小写字母替换为大写。单独使用时参数类型应为unsigned int,若传入有符号整数则输出其十六进制补码。
c输出一个字符。参数应为int,在使用时被转换为unsigned char
s输出一个字符串。参数应为指向数组的指针。
p输出指针地址。输出内容为十六进制数,包含前缀。

标志

用于控制输出的对齐、前缀等。需要紧跟在 % 后面使用。

标志字符含义
-左对齐,在指定字段宽度的右侧填充空格。
+为数据显示正负号,对unsigned型不生效。
如果输出值为非负数且没有符号则添加空格。在同时指定+标志时被忽略。
#对于八进制和十六进制分别添加0和0x/X的前缀;对于小数则强制输出小数点,即使后面没有数字。
0使用0而不是空格进行填充。如果指定了左对齐标志或对整数指定精度则被忽略。

字符宽度

一个数字,位于标志字符之后,指定输出的最小字符宽度。如果输出的字符数小于此宽度,则用空格(若指定了0标志则用0)填充。如果输出的字符超过此宽度则不会生效。

数据精度

以一个点号开头,位于字符宽度之后。当输出的数据类型为整数(转换说明字符为d, i, o, u, x, X)时,指定要显示的最小数字位数,不足时在前面补0。当输出的数据类型为浮点数(转换说明字符为f, F, e, E, a, A)时,指定小数点后的位数,不足在后面补0。当转换说明字符为gG时,指定保留的有效数字位数。当输出的数据类型为字符串时(转换说明字符为s),指定将会输出的最大字符数。

特别地,若将字符宽度和数据精度用*代替,则会采用参数列表中在输出内容之前的两项内容来代替。

长度修饰符

位于数据精度之后,转换说明符之前。用于指定输出数据的大小以便处理不同长度的数据。

长度修饰符含义
h表示shortunsigned short类型。适用于所有整数转换说明符。
hh表示signed charunsigned char类型。适用于所有整数转换说明符。
l表示longunsigned long。适用于所有整数转换说明符。
ll表示long longunsigned long long。适用于所有整数转换说明符。
L表示long double。适用于所有浮点转换说明符。
z表示size_tsizeof运算符返回)。
t表示ptrdiff_t(指针相减返回)。

示例

接下来看一些使用例:

C
// 单独使用转换说明符
#include <stdio.h>
int main(void)
{
	int a = 87;
	unsigned int b = 87;
	float c = 3.14;
	double d = 2.71;
	float e = 1919810;
	unsigned int f = 87;
	unsigned int g = 114514;
	int h = 65;
	char i[] = "Hello World.";
	long *j = &a;

	printf("%d\n", a);
	printf("%u\n", b);
	printf("%f %f\n", c, d);
	printf("%a %A %g %G\n", c, c, c * 0.00001, e);
	printf("%e\n", e);
	printf("%o\n", f);
	printf("%x %X\n", g, g);
	printf("%c\n", h);
	printf("%s\n", i);
	printf("%p", (void *)j);
	return 0;
}

输出为:

87
87
3.140000 2.710000
0x1.91eb860000000p+1 0X1.91EB860000000P+1 3.14e-05 1.91981E+06
1.919810e+06
127
1bf52 1BF52
A
Hello World.
0000008DB1B7F644
C
// 使用字符宽度和标志
#include <stdio.h>
int main(void)
{
	char str[] = "Ciallo~";
	int a = 87;
	int b = -87;
	int c = 114514;
	float d = 10;

	printf("★%-10s\n", str);
	printf("★%+10d\n", a);
	printf("★%+10d\n", b);
	printf("★% -10d\n", a);
	printf("★% -10d\n", b);
	printf("★%#10o\n", c);
	printf("★%#10x\n", c);
	printf("★%#10X\n", c);
	printf("★%10.0f\n", d);
	printf("★%#10.0f\n", d);
	printf("★%010d★", a);
	return 0;
}

输出为:

★Ciallo~   ★
★       +87★
★       -87★
★ 87       ★
★-87       ★
★   0337522★
★   0x1bf52★
★   0X1BF52★
★        10★
★       10.★
★0000000087★
C
// 使用数据精度
#include <stdio.h>
int main(void)
{
	short a = 10;
	unsigned short b = 65535;
	signed char c = -128;
	double d = 3.1415926;
	double e = 12345.678;
	double f = 0.0000000123;
	double g = 1000000000000000.0;

	printf("%.5d\n", a);
	printf("%.3d\n", b);
	printf("%.5d\n", c);
	printf("%.3f\n", d);
	printf("%.10f\n", d);
	printf("%.3G\n", d);
	printf("%.5g\n", e);
	printf("%.5G\n", f);
	printf("%.6g", g);
	return 0;
}

输出为

00010
65535
-00128
3.142
3.1415926000
3.14
12346
1.23E-08
1e+15
C
// 使用长度修饰符
#include <stdio.h>
int main(void)
{
	short a = 10;
	unsigned short b = 65535;
	signed char c = -128;
	unsigned char d = 255;
	long e = 2147483647;
	unsigned long f = 4294967294;
	long long g = 9223372036854775806;
	unsigned long long h = 18446744073709551615;
	long double i = 3.14159265358979;

	printf("%hd\n", a);
	printf("%hu\n", b);
	printf("%hhd\n", c);
	printf("%hhu\n", d);
	printf("%ld\n", e);
	printf("%lu\n", f);
	printf("%lld\n", g);
	printf("%llu\n", h);
	printf("%.15Lf\n", i);
	printf("%zd\n", sizeof h);
	printf("%td", &b - &a);
	return 0;
}

输出为:

10
65535
-128
255
2147483647
4294967294
9223372036854775806
18446744073709551615
3.141592653589790
8
16

接下来玩点花的:

C
// 大杂烩
#include <stdio.h>
int main(void)
{
	long double a = 3.1415926535;
	unsigned long long b = 219845122340;
	printf("★%+*.*llu\n", 20, 15, b);
	printf("★% #020.0Lf\n", a);
	printf("%%"); // 直接打印百分号
	return 0;
}

输出为:

★     000219845122340★
★ 000000000000000003.★
%

scanf()函数

scanf()作为处理输入的函数,其转换说明由转换说明符、抑制赋值标志、字符宽度和长度修饰符组成。scanf()函数所用的转换说明与printf()函数几乎相同,但对浮点类型和符号的处理略有差异。

转换说明符

转换说明符含义
d把输入解释为有符号十进制整数,i与其等价。单独使用时地址参数对应的类型应为int
u把输入解释为无符号十进制整数。单独使用时地址参数对应的类型应为unsigned int
f,e,a,g把输入解释为浮点数。单独使用时地址参数对应的类型应为float
o把输入解释为有符号八进制整数。单独使用时地址参数对应的类型应为int
x把输入解释为有符号十六进制整数。若改为X则适用于输入数字中的字母为大写的情形。单独使用时地址参数对应的类型应为int
c把输入解释为一个字符,不会忽略空白字符。地址参数对应的类型应为char
s把输入解释为字符串,遇到空白字符即停止。参数应为指向数组的指针。
p把输入解释为指针地址。参数应为一个指针的地址。

抑制赋值

*表示,放在%后,若使用此标志,则会在读取输入时跳过对应的内容。

C
// 使用抑制赋值标志
#include <stdio.h>
int main(void)
{
	int a;
	scanf("%*d %*d %d", a);
	return 0;
}

可接受如下输入:

10 12 15

a会被赋值为15。

字符宽度

一个数字,位于长度修饰符前,表示输入的最大字符宽度。对输入的读取会在达到此宽度或遇到空白字符时停止。

长度修饰符

位于字符宽度之后,转换说明符之前。

长度修饰符含义
h表示shortunsigned short类型。适用于所有整数转换说明符。
hh表示signed charunsigned char类型。适用于所有整数转换说明符。
l用于整数转换说明符时表示longunsigned long,用于浮点转换说明符时表示double
ll表示long longunsigned long long。适用于所有整数转换说明符。
L表示long double。适用于所有浮点转换说明符。
z表示size_tsizeof运算符返回)。
t表示ptrdiff_t(指针相减返回)。