C语言补漏
Thu Jun 26 2025 16:11:21 GMT+0800
格式化字符串
%[n$][标志][宽度][.精度][长度修饰符]类型
标志(Flags)
控制对齐、符号、填充等:
标志 | 作用 | 示例(输入值 42 ) |
---|---|---|
- |
左对齐(默认右对齐) | %-5d → 42 |
+ |
强制显示正负号 | %+d → +42 |
空格 |
正数前加空格,负数前加 - |
% d → 42 |
0 |
用 0 填充宽度(与 - 冲突时忽略) |
%05d → 00042 |
# |
添加前缀: %o → 0 、%x /%X → 0x /0X 、浮点数强制保留小数点 |
%#x → 0x2a |
宽度(Field Width)
- 数值:固定宽度(不足时填充空格或 0
,超宽则完整输出)。
- *
:动态指定宽度(从参数列表获取)。
精度(.Precision)
控制小数位数、字符串截断等:
- .数值
:指定精度。
- .*
:动态指定精度(从参数列表获取)。
说明符 | 精度作用 | 示例(输入值 3.14159 , "Hello" ) |
---|---|---|
%f |
小数位数 | %.2f → 3.14 |
%s |
最大字符数(超长截断) | %.3s → "Hel" |
%d |
最小数字位数(不足补 0 ) |
%.5d → 00042 |
长度修饰符(Length)
指定参数的数据类型长度:
修饰符 | 适用说明符 | 参数类型 |
---|---|---|
h |
%d , %u 等 |
short / unsigned short |
hh |
%d , %u |
signed char / unsigned char |
l |
%d , %f , %s |
long / wchar_t* (宽字符串) |
ll |
%d , %u |
long long |
L |
%f , %e |
long double |
类型(Types)
整数
说明符 | 数据类型 | 输出格式说明 | 示例(输入值 42 ) |
---|---|---|---|
%d |
int |
有符号十进制整数 | 42 |
%i |
int |
同 %d ,可解析八进制(0 )或十六进制(0x )输入 |
42 |
%u |
unsigned int |
无符号十进制整数 | 42 |
%o |
unsigned int |
无符号八进制(无前缀 0 ) |
52 |
%x |
unsigned int |
无符号十六进制(小写字母,无前缀 0x ) |
2a |
%X |
unsigned int |
无符号十六进制(大写字母) | 2A |
实数
说明符 | 数据类型 | 输出格式说明 | 示例(输入值 42 ) |
---|---|---|---|
%f |
float/double |
小数形式浮点数(默认6位小数) | 42.000000 |
%e |
float/double |
指数形式(小写 e ,如 1.23e+02 ) |
4.200000e+01 |
%E |
float/double |
指数形式(大写 E ) |
4.200000E+01 |
%g |
float/double |
自动选择 %f 或 %e (更简洁形式) |
42 |
%G |
float/double |
自动选择 %f 或 %E |
42 |
字符
说明符 | 数据类型 | 输出格式说明 | 示例(输入值 42 ) |
---|---|---|---|
%c |
char |
单个字符 | * (ASCII 42) |
%s |
char* |
字符串(遇 \0 结束) |
"Hello" → Hello |
%% |
- | 输出 % 本身 |
%% → % |
内存
说明符 | 数据类型 | 输出格式说明 | 示例(输入值 42 ) |
---|---|---|---|
%p |
void* |
指针地址(十六进制) | 0x7ffd8a3be52c |
%n |
int* |
不输出,存储已写字符数到参数指向的位置 | int cnt; printf("a%n", &cnt); → cnt=1 |
Scanf的规则
scanf
函数按照格式控制符逐个匹配输入数据,严格要求匹配,但是空白符较为特殊(因为在C语言中,空格、制表符和换行符都被视为分隔输入的空白字符)。
遇到%c
时,scanf
会读取一个字符,任何字符(包括空格)都会导致输入结束。
遇到%s
时,scanf
会读取一个字符串,遇到空格、制表符或换行符等空白字符时输入结束。
C 存储类
存储类定义 C 程序中变量/函数的存储位置、生命周期和作用域。
这些说明符放置在它们所修饰的类型之前。
下面列出 C 程序中可用的存储类:
- auto 存储类是所有局部变量默认的存储类。定义在函数中的变量默认为 auto 存储类,这意味着它们在函数开始时被创建,在函数结束时被销毁。
- register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个字),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。register 存储类定义存储在寄存器,所以变量的访问速度更快,但是它不能直接取地址,因为它不是存储在 RAM 中的。在需要频繁访问的变量上使用 register 存储类可以提高程序的运行速度。
- static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。静态变量在程序中只被初始化一次,即使函数被调用多次,该变量的值也不会重置。
- extern 存储类用于定义在其他文件中声明的全局变量或函数。当使用 extern 关键字时,不会为变量分配任何存储空间,而只是指示编译器该变量在其他文件中定义。extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。
C enum(枚举)
枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
文件读写
打开文件
FILE *fopen( const char *filename, const char *mode );
在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,文件内容会被清空(即文件长度被截断为0)。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
关闭文件
为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:
int fclose( FILE *fp );
如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。
C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。
写入文件
下面是把字符写入到流中的最简单的函数:
int fputc( int c, FILE *fp );
函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。您可以使用下面的函数来把一个以 null 结尾的字符串写入到流中:
int fputs( const char *s, FILE *fp );
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。您也可以使用 int fprintf(FILE *fp,const char *format, …) 函数把一个字符串写入到文件中。尝试下面的实例:
**注意:**请确保您有可用的 tmp 目录,如果不存在该目录,则需要在您的计算机上先创建该目录。
/tmp 一般是 Linux 系统上的临时目录,如果你在 Windows 系统上运行,则需要修改为本地环境中已存在的目录,例如: C:\tmp、D:\tmp等。
读取文件
下面是从文件读取单个字符的最简单的函数:
int fgetc( FILE * fp );
fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。下面的函数允许您从流中读取一个字符串:
char *fgets( char *buf, int n, FILE *fp );
函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
如果这个函数在读取最后一个字符之前就遇到一个换行符 ‘\n’ 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE *fp, const char *format, …) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
二进制 I/O 函数
下面两个函数用于二进制输入和输出:
size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
这两个函数都是用于存储块的读写 - 通常是数组或结构体。