程序入门
> 写出第一个C程序,搞懂main函数、头文件、man手册、printf缓冲区。
一、创建第一个项目(VSCode + WSL)
1. 打开WSL终端
打开VSCode里面WSL终端
2. 新建文件
在VSCode左侧目录新建 main.c
→ 输入以下代码:
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}

3. 编译 & 运行
复制
gcc main.c -o hello
./hello

看到 Hello World! 说明环境OK。
二、程序基本结构拆解
部分 | 作用 |
---|---|
#include <stdio.h> |
引入标准输入输出头文件 |
int main(void) |
程序唯一入口 |
printf("Hello World!\n"); |
向标准输出打印字符串 |
return 0; |
告诉操作系统"正常结束" |
1. 头文件是什么?
声明函数、宏、类型,供编译器检查
查man手册可快速确认需要哪个头文件:
man 3 printf

顶部即显示:#include <stdio.h>
#include <stdio.h>
int printf ( const char *format, ...); // 默认把数据按照format的格式输出到标准输出文件中
int fprintf (FILE *stream, const char *format, ...); // 往指定的文件流中输出stream
int dprintf (int fd, const char *format, ...); // 往指定的文件中输出fd
int sprintf (char *str, const char *format, ...); // 往指定的地址中输出
int snprintf(char *str, size_t size, const char *format, ...); // 【推荐】功能与sprintf一样但是多了大小控制,可以避免越界的错误
通过函数原型分析所需的参数
int printf(const char *format, ...);
/*参数分析:
format -->格式控制符,通过格式控制符进行回溯并解析得到正确的数据 %d %c %s %f ....*/

2. main函数细节
有且仅有一个,返回类型必须是 int
两种写法:
int main(void) { ... } // 无参数
int main(int argc, char *argv[]) { ... } // 带命令行参数
return 0;
代表正常退出,非0表示异常
三、printf缓冲区实验
1. 行缓冲现象
#include <stdio.h>
int main(void)
{
printf("123"); // 无`\n`,不会立即显示
while(1); // 死循环,程序不退
return 0;
}

运行结果:屏幕无输出
原因:stdout
是行缓冲,遇到\n
或缓冲区满才刷新
2. 强制刷新
#include <stdio.h>
int main(void)
{
printf("123");
fflush(stdout); // 立即刷新
while(1);
}

此时立刻看到123
四、关于格式控制符
基本格式控制符
格式控制符 | 含义 | 适用数据类型(示例) | printf 示例 | 输出结果(示例) |
---|---|---|---|---|
%d |
有符号十进制整数 | int , short |
printf("%d", 10); |
10 |
%i |
有符号十进制整数 (与 %d 基本相同) |
int , short |
printf("%i", -5); |
-5 |
%u |
无符号十进制整数 | unsigned int |
printf("%u", 10); |
10 |
%o |
无符号八进制整数 (不输出前缀 0) | unsigned int |
printf("%o", 10); |
12 |
%x |
无符号十六进制整数(小写字母) (不输出前缀 0x) | unsigned int |
printf("%x", 255); |
ff |
%X |
无符号十六进制整数(大写字母) (不输出前缀 0X) | unsigned int |
printf("%X", 255); |
FF |
%f |
单精度浮点数(小数形式) | float |
printf("%f", 3.14); |
3.140000 |
%lf |
双精度浮点数(小数形式) (在 printf 中 %f 和 %lf 无区别) |
double |
printf("%f", 3.14); |
3.140000 |
%e |
以指数形式输出浮点数(小写 e) | float , double |
printf("%e", 300.0); |
3.000000e+02 |
%E |
以指数形式输出浮点数(大写 E) | float , double |
printf("%E", 300.0); |
3.000000E+02 |
%g |
自动选择 %f 或 %e 中更短的一种格式(不输出无意义的0) |
float , double |
printf("%g", 3.0); |
3 |
%G |
自动选择 %f 或 %E 中更短的一种格式(不输出无意义的0) |
float , double |
printf("%G", 0.0003); |
0.0003 |
%c |
单个字符 | char |
printf("%c", 'A'); |
A |
%s |
字符串 | char * (字符串指针) |
printf("%s", "Hi"); |
Hi |
%p |
输出指针地址(以十六进制形式) | void * (任何指针) |
int a; printf("%p", &a); |
0x7ffd42a1b23c (地址值) |
%% |
输出一个百分号 % |
(无) | printf("%%"); |
% |
附加格式修饰符
基本语法: %[flags][width][.precision]specifier
修饰符 | 含义 | 示例 | 输出结果(示例) |
---|---|---|---|
%md |
输出宽度为 m,不足宽度用空格填充,右对齐 | printf("%5d", 10); |
10 |
%-md |
输出宽度为 m,不足宽度用空格填充,左对齐 | printf("%-5d", 10); |
10 |
%0md |
输出宽度为 m,不足宽度用 0 填充 | printf("%05d", 10); |
00010 |
%m.nf |
输出宽度为 m,保留 n 位小数 | printf("%8.2f", 3.14159); |
3.14 |
%.nf |
只指定保留 n 位小数,宽度不限 | printf("%.3f", 3.14); |
3.140 |
%*.*f |
宽度和小数位数由参数指定 | printf("%*.*f", 8, 2, 3.14); |
3.14 |
重要注意事项
-
scanf
与printf
的区别:- 在
scanf
中,%f
用于读取float
类型,而%lf
用于读取double
类型 - 在
printf
中,%f
和%lf
对于double
类型没有区别
- 在
-
输出结果的变异性:
- 表格中的"输出结果"是典型示例
- 实际输出可能因编译器、系统环境或参数的不同而略有差异
- 特别是地址值 (
%p
)、浮点数精度等可能会有不同表现
五、man手册常用命令
命令 | 含义 |
---|---|
man 1 printf |
查看Shell命令printf |
man 2 open |
查看系统调用open |
man 3 printf |
查看库函数printf |
man 4 null |
查看特殊设备文件 |
man 5 passwd |
查看文件格式和配置文件 |
man 6 intro |
查看游戏相关 |
man 7 signal |
查看系统信号和宏定义 |
man 8 mount |
查看系统管理命令 |
man 9 EXT4 |
查看内核相关文档 |
man -f printf |
列出所有分册包含printf |
九大分册详解:
分册 | 内容类型 | 典型示例 |
---|---|---|
1 | 用户命令(可执行程序) | ls , gcc , printf |
2 | 系统调用(内核接口) | open , read , fork |
3 | 库函数(C标准库) | printf , malloc , fopen |
4 | 特殊文件(设备文件) | null , zero , random |
5 | 文件格式(配置文件) | passwd , fstab , hosts |
6 | 游戏 | 已废弃,很少使用 |
7 | 杂项(协议/文件系统) | signal , tcp , udp |
8 | 系统管理(管理员命令) | mount , ifconfig , useradd |
9 | 内核例程(内核API) | 内核开发者使用 |
嵌入式开发常用分册:
- 1、2、3 - 日常开发必备
- 5、7 - 配置文件和协议参考
- 8 - 系统管理和调试