【C】 06 - 标准库概述
任何程序都会有一些通用的功能需求,对这些需求的实现组成了库。它可以提高程序的复用性、健壮性和可移植性,这也是模块化设计的体现。C规范定义了一些通用接口库,这里只作概述性介绍,具体细节当然还是要查阅规范。
为了提高效率,C的很多库函数会同时有一个宏定义的版本,所以传递参数时尽量使用没有副作用的表达式,以免发生错误。若不想使用宏版本的函数,可以采用三个方法:(1)先undef宏;(2)函数名用括号括起来;(3)不include头文件。
// method 1 #include <stdlib.h> #undef atoi atoi("123"); // method 2 #include <stdlib.h> (atoi)("123"); // method 3 extern int atoi(const char*); atoi("123");
输入输出库<stdio.h>包含对文件的操作、对流的控制、输入输出。C的所有读写对象被称为流,它的所有信息包含在结构体FILE里,包含buffer、位置信息等。系统有3个类型为FILE*的表达式(stdin、stdout、stderr)指向预定义的流,分别是scanf、printf、assert的默认流。库中对文件的操作包括删除、重命名、打开、关闭、buffer设置等。对流的控制包括刷新、位置获取和设置、错误处理等。输入输出包括直接输入输出、字符和字符串的输入输出、格式化输入输出等,相对应的宽字符输入输出在<wchar.h>中定义。标准库对buffer边界的检查不是很严格,如需更安全的接口,请查看ISO/IEC TR 24731。以printf为例,格式化输出提供了度量类型的可控格式输出。scanf的格式比printf简单,不作介绍,仅需注意其中空白仅起分割作用。每个格式的形式为%[flags][width][.precision][length]type,方括号为可选,具体意义如下表所示。参数传递时会先做整型提升、float转为double、指针转为void*,length和type共同决定最终输出的类型,而不是原始数据的类型。
flag | - | 左对齐, 默认右对齐 |
+ | 显示符号, 默认仅显示负号 | |
空格 | 不显示符号的正数符号位用空格 | |
# | 8/16进制显示前缀, 浮点数总显示小数点 | |
0 | 整数和浮点前导0填充, 若左对齐或整数有精度则不填充 | |
width | 正整数, * | 显示的最小宽度, *表示最小宽度由参数决定 |
precision | 正整数, 空 | 整数的最少数字, g/G的最多有效位, 其它浮点数的小数位数, 为空表示0 |
length | hh, h, l, ll, j, z, t | 整数分别为char/short/long/long long/intmax_t/size_t/ptrdiff_t, n为相应指针, lc为wint_t, ls为wchar_t |
L | 浮点数为long double | |
type | d, i, o, u, x, X | 整数, d/i为有符号整数, o/u/x/X为8/10/16进制无符号整数 |
f, F, e, E, g, G, a, A | 浮点数, f/F为10进制浮点, e/E为10进制科学记数法, g/G为前两者较短者, a/A为16进制科学记数法 | |
c, s | c为字符, s为字符串 | |
p | 指针 | |
n | 向整型指针参数写入已打印的字符数 | |
% | % |
可变参数库<stdarg.h>旧名<varargs.h>,提供了一些宏获取隐式参数。函数参数的压栈顺序是基于实现的,所以编译器也要负责可变参数的管理。参数序列的信息的地址存放在va_list(指针)里,它通过va_start从参数中获取,或者用va_copy拷贝得来。va_arg从列表头部获取某个类型的参数,取完后从列表中删除。va_clean彻底释放列表信息。
// interfaces of <stdarg.h> void va_start(va_list ap, paraN); void va_copy(va_list dst, va_list src); type va_arg(va_list ap, type); void va_end(va_list ap); // example #include <stdarg.h> #include <stdio.h> void fun(int n, ...) { va_list va, copy; int a; va_start(va, n); va_copy(copy, va); a = va_arg(va, int); va_end(va); va_end(copy); }
通用函数库<stdlib.h>提供了非常多的常用接口,它原本也是标准库的大本营。包含字符串转为数、随机数生成、动态内存管理、系统控制和调用、排序查找算法、整数的绝对值和除法、宽字符和多字节字符的转换等。通用宏定义<stddef.h>包含一些常用宏定义,包含ptrdiff_t、size_t、wchar_t、NULL、offsetof、max_align_t等。时间库<time.h>包含了时间和日期的获取和格式化打印,本地化库<local.h>可以将C配置成符合各地区习惯的书写风格,默认在初始化时设置为setlocal(LC_ALL,"C")。
// some interfaces in <stdlib.h> int atoi(const char* nptr); double atof(const char* nptr); int rand(void); void srand(unsigned int seed); void* malloc(size_t size); void* realloc(void* ptr, size_t size); _Noreturn void abort(void); int system(const char* string); void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); int abs(int j); div_t div(int numer, int denom); int mbtowc(wchar_t* restrict pwc, const char* restrict s, size_t n);
范围库<limits.h>定义了各类整型的取值范围,整数类型库<stdint.h>定义了各种长度要求的整型及其取值范围。整数类型库<inttypes.h>包含<stdint.h>,另外还有为各种整型在格式化输入输出定义了格式宏,以及这些整型的对应的通用库(<stdlib.h>)。浮点库<float.h>定义平台浮点表示的具体细节,包括各个域的范围等。浮点环境库<fenv.h>包括浮点运算环境的获取和配置,以及当前浮点运算的状态和异常信息等。
// some macros in <limits.h> #define CHAR_BIT 8 #define SCHAR_MIN -128 #define INT_MAX (-2147483647 - 1) // some macros in <stdint.h> #define int32_t int #define intmax_t long long #define INT32_MAX INT_MAX // some macros in <inttypes.h> #include <stdint.h> #define PRId64 "lld" intmax_t imaxabs(intmax_t j); intmax_t strtoimax(const char* restrict nptr, char** restrict endptr, int base);
数学函数库<math.h>定义了各种浮点数的数学函数,复数函数库<complex.h>定义了复数相关的宏以及复数的数学函数,<tgmath.h>包含了前两者,并为相同的函数使用了统一的宏(名称和中<math.h>一样)。
字符类型库<ctype.h>和宽字符类型库<wctype.h>提供了接口判断字符的类型,还可以进行大小写转换。字符串库<string.h>提供了对字符串(内存段)的操作,包括复制、连接、查找等。宽字符库<wchar.h>包含各种函数的宽字符版本,包括格式化输入输出、字符串操作、时间显示、宽字符与单字符的转换等。unicode库<uchar.h>包括对unicode的操作和转换,符号库<iso646.h>为iso_646中没有的符号提供了单词表示,比如and表示&&。
// some interfaces in <ctype.h> int isdigit(int c); int islower(int c); int toupper(int c); // some interfaces in <string.h> void* memcpy(void* restrict s1, const void* restrict s2, size_t n); char* strcpy(char* restrict s1, const char* restrict s2); char* strcat(char* restrict s1, const char* restrict s2); int memcmp(const void* s1, const void* s2, size_t n); char* strstr(const char* s1, const char* s2);
长跳转库<setjmp.h>提供了函数间直接跳转的接口,用于快速回到旧的执行点。jmp_buf是个数组类型,它保存了跳转的必要信息。setjmp保存当前点,longjmp跳转到某个时间点。setjmp返回0表示是从setjmp返回的,否则是从longjmp跳转的。信号库<signal.h>提供了向系统注册信号回调函数和发起信号的接口,检查库<assert.h>在运行或编译时检查条件并报错,错误号库<errno.h>中定义的全局变量记录了上一次出错的编号。
#include <setjmp.h> jmp_buf buf; void f(void) {g();} void g(void) {longjmp(buf, 1);} int main (void) { if (0 == setjmp(buf)) f(); // from setjmp else {} // from longjmp }
库<stdbool.h>、<stdalign.h>和<stdnoreturn.h>为新加的关键字定义了全小写的宏。库<threads.h>和<stdatomic.h>提供了线程和原子操作相关的宏和接口,相应内容在这里已被全部忽略(没仔细看,也没看懂),请参考规范相应内容(包括关键字_Thread_local和_Atomic的使用)。
【全篇完】