printf 笔记
1. 基本概念
printf 是 C 标准库函数,声明在 <stdio.h> 中,用于把格式化文本输出到标准输出 stdout。
函数原型:
int printf(const char *format, ...);
format:格式字符串...:可变参数,数量和类型由格式字符串决定- 返回值:成功时返回输出的字符数;失败时返回负数
printf不会自动换行,需要手动写\n
最简示例:
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
2. 格式说明符
格式说明符以 % 开头,常见如下:
| 说明符 | 含义 | 参数类型 | 示例 |
|---|---|---|---|
%d / %i |
有符号十进制整数 | int |
printf("%d", 42); |
%u |
无符号十进制整数 | unsigned int |
printf("%u", 42u); |
%o |
无符号八进制 | unsigned int |
printf("%o", 255); → 377 |
%x / %X |
无符号十六进制 | unsigned int |
printf("%x", 255); → ff |
%f |
浮点数,默认 6 位小数 | double |
printf("%f", 3.14); |
%e / %E |
科学计数法 | double |
printf("%e", 3.14); |
%g / %G |
自动选择 %f 或 %e |
double |
printf("%g", 3.14); |
%c |
单个字符 | int,通常传 char |
printf("%c", 'A'); |
%s |
字符串 | char * |
printf("%s", "hello"); |
%p |
指针地址 | void * |
printf("%p", (void *)&x); |
%% |
输出一个 % |
不消耗参数 | printf("%%"); |
注意:
float传给printf时会自动提升为double,所以用%f即可。%s要求字符串必须以'\0'结尾。%c的参数是int,但传char通常也可以。
3. 长度修饰符
用于说明整数或浮点数的具体长度。
| 修饰符 | 配合 | 含义 |
|---|---|---|
hh |
d i u o x X |
signed char / unsigned char |
h |
d i u o x X |
short / unsigned short |
l |
d i u o x X |
long / unsigned long |
ll |
d i u o x X |
long long / unsigned long long |
z |
d i u o x X |
size_t 或对应有符号类型 |
j |
d i u o x X |
intmax_t / uintmax_t |
t |
d i u o x X |
ptrdiff_t |
L |
f e g |
long double |
常见例子:
long a = 123L;
long long b = 456LL;
size_t len = strlen("hello");
printf("%ld\n", a);
printf("%lld\n", b);
printf("%zu\n", len);
printf("%Lf\n", 3.14L);
4. 标志、宽度和精度
完整格式:
%[标志][宽度][.精度][长度]说明符
常用标志
| 标志 | 作用 | 示例 |
|---|---|---|
- |
左对齐 | `printf("%-5d |
+ |
正数也显示 + |
printf("%+d", 5); → +5 |
| 空格 | 正数前显示空格 | printf("% d", 5); → 5 |
0 |
用 0 填充宽度 |
printf("%05d", 42); → 00042 |
# |
替代形式 | printf("%#x", 255); → 0xff |
宽度
宽度表示最少输出多少个字符,不够时补空格。
printf("%5d\n", 42); // " 42"
printf("%-5d|\n", 42); // "42 |"
printf("%05d\n", 42); // "00042"
精度
精度以 . 开头。
- 对整数:最少位数,不够补
0 - 对浮点数:小数点后位数
- 对字符串:最多输出多少个字符
printf("%.3d\n", 42); // "042"
printf("%.2f\n", 3.14159); // "3.14"
printf("%.3s\n", "hello"); // "hel"
动态宽度和精度
用 * 从参数中读取宽度或精度。
int width = 8;
int precision = 2;
printf("%*d\n", width, 42); // " 42"
printf("%-*d|\n", width, 42); // "42 |"
printf("%.*f\n", precision, 3.14159); // "3.14"
printf("%*.*f\n", width, precision, 3.14159);
5. 转义字符
printf 的格式字符串中可以使用转义字符:
| 转义 | 含义 |
|---|---|
\n |
换行 |
\t |
水平制表 |
\r |
回车 |
\b |
退格 |
\f |
换页 |
\v |
垂直制表 |
\\ |
反斜杠 |
\' |
单引号 |
\" |
双引号 |
\? |
问号 |
\0 |
空字符 |
\ooo |
八进制字符,最多三位 |
\xhh |
十六进制字符 |
示例:
printf("Hello\nWorld\n");
printf("Name:\tTom\n");
printf("100%%\n"); // 输出 100%
printf("A = \x41\n"); // 输出 A = A
6. 常见用法示例
输出整数
int age = 20;
printf("Age: %d\n", age);
printf("Hex: %x\n", 255);
printf("Oct: %o\n", 255);
输出浮点数
double pi = 3.14159;
printf("%f\n", pi); // 3.141590
printf("%.2f\n", pi); // 3.14
printf("%8.2f\n", pi); // " 3.14"
printf("%-8.2f|\n", pi); // "3.14 |"
输出字符和字符串
char grade = 'A';
char name[] = "Tom";
printf("%c\n", grade);
printf("%s\n", name);
printf("%.1s\n", name); // "T"
输出指针
int x = 10;
printf("%p\n", (void *)&x);
输出百分号
printf("Discount: 20%%\n");
7. printf 的返回值
printf 返回成功输出的字符数,不包括结尾的 '\0',因为 printf 不会自动添加 '\0'。
int n = printf("abc\n");
printf("%d\n", n); // 4,因为 'a'、'b'、'c'、'\n' 共 4 个字符
如果输出失败,返回负数。
8. 常见错误
格式说明符和参数类型不匹配
printf("%d\n", 3.14); // 错误:应该用 %f
printf("%f\n", 42); // 错误:应该用 %d
printf("%s\n", 'A'); // 错误:应该用 %c
printf("%c\n", "A"); // 错误:应该用 %s 或传 'A'
格式不匹配属于未定义行为,可能输出乱码、崩溃或看似正常但结果不可靠。
忘记换行
printf("Hello");
printf("World");
输出:
HelloWorld
应该写成:
printf("Hello\n");
printf("World\n");
字符串没有 '\0' 结尾
char s[3] = {'c', 'a', 't'};
printf("%s\n", s); // 危险,可能一直打印到内存中下一个 '\0'
应该:
char s[4] = {'c', 'a', 't', '\0'};
格式字符串漏洞
不要这样写:
printf(user_input);
如果用户输入 %s、%x 等,printf 会去栈上读取参数,可能导致崩溃或泄露信息。
应该:
printf("%s", user_input);
%n 不安全
%n 会把已输出字符数写入一个 int *,容易造成安全问题,通常不建议使用。
9. CS50 中的 printf
CS50 的 string 本质上是 char *,来自:
#include <cs50.h>
所以可以直接用 %s 打印:
#include <cs50.h>
#include <stdio.h>
int main(void)
{
string name = get_string("Name: ");
printf("Hello, %s\n", name);
}
输入:
Tom
输出:
Hello, Tom
10. 相关输出函数
| 函数 | 作用 |
|---|---|
printf |
输出到 stdout |
fprintf |
输出到指定文件流 |
sprintf |
输出到字符串,不安全 |
snprintf |
输出到字符串,带大小限制,更安全 |
puts |
输出字符串并自动换行 |
putchar |
输出单个字符 |
示例:
fprintf(stdout, "Hello\n"); // 等价于 printf("Hello\n");
char buf[100];
snprintf(buf, sizeof(buf), "%d", 42);
puts(buf);
11. 最需要记住的写法
printf("%d\n", 42);
printf("%f\n", 3.14);
printf("%.2f\n", 3.14159);
printf("%c\n", 'A');
printf("%s\n", "hello");
printf("%5d\n", 42);
printf("%-5d|\n", 42);
printf("%05d\n", 42);
printf("%x\n", 255);
printf("%p\n", (void *)&x);
printf("%%\n");
printf("%zu\n", strlen("hello"));
12. 一句话总结
printf 按格式字符串把数据转换成文本并输出到 stdout;使用时最重要的是:格式说明符必须和参数类型匹配,并且字符串要以 '\0' 结尾。