C 标准库速览
C 的强大很大程度上来自它的标准库——一组随编译器附带的、覆盖常见任务的函数。本篇是 C 标准库的速览地图,帮你建立"遇到什么问题用什么库"的索引。每个库前面章节基本都细讲过,这里做个汇总,方便日后查阅。
一、stdio.h — 标准输入输出
最常用的库,提供屏幕打印、键盘读取、文件 I/O。前面语法篇、文件 I/O 篇都详细讲过:
#include <stdio.h>
// stdio.h - 标准输入输出
int main(void) {
// 输出
printf("格式化输出\n");
fprintf(stderr, "错误输出\n");
putchar('A');
fputs("字符串\n", stdout);
// 输入
int n;
scanf("%d", &n);
char line[100];
fgets(line, sizeof(line), stdin);
// 文件 I/O
FILE *fp = fopen("x.txt", "r");
fclose(fp);
// 其他
sprintf(line, "n=%d", n); // 写入字符串
sscanf(line, "n=%d", &n); // 从字符串解析
remove("临时文件.txt"); // 删除文件
rename("旧名.txt", "新名.txt"); // 重命名
return 0;
}核心函数:printf/scanf(屏幕)、fopen/fread/fclose(文件)、sprintf/sscanf(字符串解析)。三个标准流:stdin(键盘)、stdout(屏幕)、stderr(错误)。
二、stdlib.h — 通用工具
杂七杂八的实用函数都在这里:内存管理、随机数、排序、字符串转数字、程序控制。
#include <stdio.h>
#include <stdlib.h> // 通用工具库
int compare(const void *a, const void *b) {
return *(int*)a - *(int*)b; // 升序比较函数
}
int main(void) {
// 1. 内存管理(详见 memory 篇)
int *arr = malloc(10 * sizeof(int));
free(arr);
// 2. 随机数
srand(time(NULL)); // 播种子(只调一次)
int r = rand() % 100; // 0~99 的随机数
printf("随机: %d\n", r);
// 3. qsort:快速排序(对任意类型数组)
int nums[] = {5, 2, 8, 1, 9, 3};
int n = sizeof(nums) / sizeof(nums[0]);
qsort(nums, n, sizeof(int), compare);
// nums 现在是 {1, 2, 3, 5, 8, 9}
// 4. 字符串转数字
int i = atoi("42"); // 字符串转 int
long l = atol("1000000"); // 转 long
double d = atof("3.14"); // 转 double
// 推荐用更安全的 strtol/strtod(能检测错误)
char *end;
long val = strtol("123abc", &end, 10);
printf("val=%ld, 剩余=%s\n", val, end); // val=123, 剩余=abc
// 5. 程序控制
// exit(0); // 立即退出程序(0 正常, 非0 错误)
// abort(); // 异常终止(产生 core dump)
// atexit(fn); // 注册退出时调用的函数
// 6. 绝对值
printf("%d\n", abs(-5)); // int 绝对值
return 0;
}重点掌握:
- 内存管理:
malloc/calloc/realloc/free(详见动态内存篇)。 - qsort:C 标准库自带的快速排序,能对任意类型数组排序——通过函数指针传入比较函数。这是函数指针最经典的应用。
- 字符串转数字:
atoi简单但不能检测错误;推荐strtol/strtod,能通过endptr知道转换在哪停。 - 随机数:
srand播种子(通常用time(NULL)),rand取随机数。注意rand质量不高,密码学用途要用别的库。
三、string.h — 字符串与内存
前面字符串篇细讲过。除了字符串函数,还有几个对任意内存操作的函数:
#include <stdio.h>
#include <string.h> // 字符串函数(详见 strings 篇)
int main(void) {
// 1. 长度
size_t len = strlen("hello"); // 5
// 2. 复制
char buf[20];
strcpy(buf, "hello");
strncpy(buf, "hello", sizeof(buf) - 1);
// 3. 拼接
strcat(buf, " world");
strncat(buf, "!", 1);
// 4. 比较
if (strcmp("a", "b") < 0) printf("a < b\n");
if (strncmp("abc", "abd", 2) == 0) printf("前2个相同\n");
// 5. 查找
char *p = strchr("hello", 'l'); // 找字符
char *sub = strstr("hello world", "world"); // 找子串
// 6. 内存操作(对任意数据,不止字符串)
int a[5] = {1, 2, 3, 4, 5};
int b[5];
memcpy(b, a, sizeof(a)); // 内存拷贝
memset(b, 0, sizeof(b)); // 清零
// 7. 分割
char str[] = "a,b,c,d";
char *tok = strtok(str, ",");
while (tok != NULL) {
printf("%s\n", tok);
tok = strtok(NULL, ",");
}
return 0;
}关键函数:
- 字符串:
strlen/strcpy/strncpy/strcat/strcmp/strchr/strstr/strtok。 - 内存:
memcpy(拷贝)、memset(填充,常用memset(buf, 0, n)清零)、memcmp(比较)、memmove(重叠区域拷贝安全)。
四、math.h — 数学函数
提供数学运算函数。注意编译时要加 -lm 链接数学库(gcc hello.c -lm):
#include <stdio.h>
#include <math.h> // 数学函数(编译时要加 -lm 链接库)
#include <stdlib.h>
int main(void) {
// 基本运算
printf("%.2f\n", pow(2, 10)); // 1024.00 (2的10次方)
printf("%.2f\n", sqrt(16)); // 4.00 (平方根)
printf("%.2f\n", cbrt(27)); // 3.00 (立方根)
printf("%.2f\n", fabs(-3.14)); // 3.14 (绝对值)
// 取整
printf("%.2f\n", ceil(3.2)); // 4.00 (向上取整)
printf("%.2f\n", floor(3.8)); // 3.00 (向下取整)
printf("%ld\n", lround(3.6)); // 4 (四舍五入)
// 对数与指数
printf("%.2f\n", log(2.71828)); // 1.00 (自然对数)
printf("%.2f\n", log10(1000)); // 3.00 (常用对数)
printf("%.2f\n", exp(1)); // 2.72 (e的1次方)
// 三角函数(参数是弧度,不是度)
printf("%.4f\n", sin(3.14159 / 2)); // 1.0000
printf("%.4f\n", cos(0)); // 1.0000
printf("%.4f\n", tan(0.785)); // 接近 1(45度)
// 常量
printf("PI = %.6f\n", M_PI); // 3.141593 (GNU 扩展)
printf("E = %.6f\n", M_E); // 2.718282
// 浮点特殊值
printf("%f\n", INFINITY); // inf
printf("%f\n", NAN); // nan
return 0;
}分类记忆:
- 幂与根:
pow、sqrt、cbrt(立方根)、hypot(勾股)。 - 取整:
ceil(向上)、floor(向下)、round(四舍五入)、trunc(截断)。 - 对数指数:
log、log2、log10、exp。 - 三角函数:
sin/cos/tan/asin/atan2(参数是弧度)。 - 特殊值:
INFINITY、NAN(Not a Number)、isinf/isnan判断。
五、time.h — 时间日期
提供时间获取、格式化、计时功能:
#include <stdio.h>
#include <time.h> // 时间函数
int main(void) {
// 1. 时间戳(Unix 时间,从 1970-01-01 起的秒数)
time_t now = time(NULL);
printf("当前时间戳: %ld\n", now);
// 2. 转成可读格式
char *s = ctime(&now);
printf("ctime: %s", s); // 如:Tue Aug 5 15:30:00 2025
// 3. 拆解成结构体
struct tm *local = localtime(&now);
printf("%d-%02d-%02d %02d:%02d:%02d\n",
local->tm_year + 1900, // 年份从 1900 开始
local->tm_mon + 1, // 月份从 0 开始
local->tm_mday,
local->tm_hour, local->tm_min, local->tm_sec);
// 4. 格式化输出
char buf[100];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
printf("格式化: %s\n", buf);
// 5. 计时(测代码运行时间)
clock_t start = clock();
// ... 一些操作 ...
for (volatile int i = 0; i < 1000000; i++);
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("耗时: %.3f 秒\n", elapsed);
return 0;
}常见用途:
- 取当前时间:
time(NULL)返回 Unix 时间戳(1970-01-01 起的秒数)。 - 格式化:
ctime(简单)、strftime(自定义格式,占位符类似 printf)。 - 结构体:
struct tm拆解成年月日时分秒,localtime(本地时区)、gmtime(UTC)。 - 计时:
clock()测 CPU 时间(精度毫秒级);更高精度用clock_gettime(POSIX)。
六、其他常用头文件
#include <stdio.h>
#include <ctype.h> // 字符判断
#include <assert.h> // 断言
#include <stdbool.h> // bool 类型(C99)
#include <stdint.h> // 固定宽度整数(C99)
#include <errno.h> // 错误码
int main(void) {
// ctype.h:字符处理
printf("%d\n", isdigit('5')); // 非0(真)
printf("%d\n", isalpha('A')); // 非0(真)
printf("%c\n", toupper('a')); // A
printf("%c\n", tolower('Z')); // z
// assert.h:断言(调试利器)
int x = 10;
assert(x > 0); // 条件为假时程序终止并打印信息
// NDEBUG 宏定义时 assert 失效(#define NDEBUG)
// 发布版本常用 -DNDEBUG 关闭所有 assert
// stdbool.h:布尔类型(C99)
bool ok = true;
if (!ok) printf("失败\n");
// stdint.h:固定宽度整数(C99)
int32_t i32 = 100; // 保证 32 位
uint64_t u64 = 1000ULL; // 保证 64 位无符号
size_t sz = sizeof(i32); // 内存大小类型
// limits.h / float.h:类型范围
// INT_MAX, INT_MIN, UINT_MAX, DBL_MAX ...
return 0;
}- ctype.h:字符判断/转换,如
isdigit、isalpha、toupper。 - assert.h:
assert(条件),条件为假时打印位置并终止。调试利器,发布版用-DNDEBUG关闭。 - stdbool.h(C99):
bool、true、false(之前 C 只能用 int 模拟)。 - stdint.h(C99):固定宽度整数
int32_t、uint64_t,跨平台必备。 - limits.h / float.h:类型范围常量
INT_MAX、DBL_MAX等。 - errno.h:全局错误码,配合
perror/strerror使用。
七、几个进阶库(知道存在即可)
- pthread.h(POSIX):多线程编程,pthread_create/pthread_join。
- setjmp.h:非局部跳转,模拟"异常"(很少用)。
- signal.h:信号处理(Ctrl+C、段错误等)。
- locale.h:本地化(国际化)。
- iso646.h:运算符别名(
and代替&&,基本没人用)。 - complex.h(C99):复数运算。
- threads.h(C11):标准线程库(替代 pthread,但支持度还不高)。
八、查文档的方法
C 标准库函数很多,没人能全记住。学会查文档:
- Linux/macOS 终端:
man printf、man malloc——man 手册是最权威的。 - cppreference.com:最全的在线 C/C++ 参考(英文名但内容覆盖 C)。
- VS Code 悬停:装上 C/C++ 扩展后,鼠标悬停在函数名上能看到签名和简短说明。
九、C 标准库 vs 第三方库
C 标准库很小——没有网络、没有正则(直到 C11 加了 stdregex,但几乎没人用)、没有 JSON、没有 HTTP。这些需求都要第三方库:
- 网络:libcurl(HTTP 客户端)、libuv(异步 I/O,Node.js 底层)。
- 数据结构:glib(哈希表、链表等)。
- JSON:cJSON、jansson。
- 正则:PCRE。
- 日志:log4c、zlog。
- 单元测试:Check、Unity。
这也是 C 的"哲学":标准库只提供最基础的积木,其他交给你选择。这种灵活也意味着 C 项目的依赖管理比 Node(pnpm)或 Python(pip)复杂——通常用 autotools/cmake/meson 等构建系统处理。
系列完结
恭喜你完成了 C 语言的 16 篇入门系列!现在你应该能:
- 读懂任何中等规模的 C 代码(开源项目、内核片段)
- 独立写出命令行工具、文件处理脚本、数据结构(链表/树)
- 用 gdb 调试、用 valgrind 查内存问题
- 理解指针、内存、栈堆——这些概念会贯穿你后续学 C++/Go/Rust
下一步建议:读 K&R《C 程序设计语言》(经典)、读 Redis 源码(极优雅的 C)、学操作系统(CSAPP)、或转 C++ 学面向对象和 STL。无论选哪条路,C 给你打的地基都会让你受益终身。
← 上一篇 C 文件 I/O
返回 C 语言教程目录