C 语言常用库
内容简介
C 标准库不是“函数大全”,而是 C 业务开发的基础工具箱。它覆盖输入输出、文件、字符串、内存、动态分配、排序查找、数学计算、时间、错误处理、断言、字符处理、固定宽度整数等能力。
学习顺序建议:
stdio.h:输入输出和文件。stdlib.h:内存、转换、排序、查找、程序控制。string.h:字符串和内存块操作。ctype.h:字符分类和大小写转换。errno.h:错误码处理。time.h:时间和日期。stdint.h/stdbool.h/stddef.h:更清晰的基础类型。assert.h/math.h/stdarg.h:调试、数学、可变参数。
用法
stdio.h:标准输入输出和文件
常用能力:
| 函数 | 用途 |
|---|---|
printf / fprintf / snprintf | 格式化输出 |
scanf / fscanf / sscanf | 格式化输入 |
fgets / fputs | 按行读写文本 |
fopen / fclose | 打开和关闭文件 |
fread / fwrite | 二进制读写 |
fflush | 刷新输出缓冲 |
perror | 输出最近一次错误信息 |
按行读取文件:
#include <stdio.h>
int main(void)
{
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
char line[256];
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line);
}
fclose(fp);
return 0;
}安全格式化字符串:
char path[128];
snprintf(path, sizeof(path), "%s/%s", "logs", "app.log");stdlib.h:内存、转换、排序、程序控制
常用能力:
| 函数 | 用途 |
|---|---|
malloc / calloc / realloc / free | 动态内存 |
atoi / strtol / strtod | 字符串转数字 |
qsort / bsearch | 排序和二分查找 |
rand / srand | 简单随机数 |
exit / atexit | 程序退出和退出回调 |
getenv / system | 环境变量和系统命令 |
字符串转整数,推荐 strtol 而不是 atoi:
#include <errno.h>
#include <stdlib.h>
int parse_int(const char *text, int *out)
{
char *end = NULL;
errno = 0;
long value = strtol(text, &end, 10);
if (errno != 0 || end == text || *end != '\0') {
return 0;
}
*out = (int)value;
return 1;
}排序:
#include <stdlib.h>
int compare_int(const void *a, const void *b)
{
int lhs = *(const int*)a;
int rhs = *(const int*)b;
return (lhs > rhs) - (lhs < rhs);
}
int arr[] = {3, 1, 2};
qsort(arr, 3, sizeof(arr[0]), compare_int);动态数组扩容:
int *new_data = realloc(data, sizeof(int) * new_count);
if (new_data == NULL) {
free(data);
return 1;
}
data = new_data;string.h:字符串和内存块
常用能力:
| 函数 | 用途 |
|---|---|
strlen | 字符串长度,不含 '\0' |
strcmp / strncmp | 字符串比较 |
strcpy / strncpy | 字符串复制 |
strcat / strncat | 字符串拼接 |
strchr / strrchr | 查找字符 |
strstr | 查找子串 |
strtok | 分割字符串 |
memcpy / memmove | 内存复制 |
memset | 内存填充 |
memcmp | 内存比较 |
strerror | 错误码转字符串 |
字符串比较:
if (strcmp(command, "start") == 0) {
puts("start service");
}查找子串:
const char *text = "user=alice;role=admin";
const char *pos = strstr(text, "role=");
if (pos != NULL) {
printf("%s\n", pos);
}内存复制:
int src[] = {1, 2, 3};
int dst[3];
memcpy(dst, src, sizeof(src));ctype.h:字符分类和转换
常用能力:
| 函数 | 用途 |
|---|---|
isdigit | 是否数字 |
isalpha | 是否字母 |
isalnum | 是否字母或数字 |
isspace | 是否空白字符 |
islower / isupper | 是否小写/大写 |
tolower / toupper | 大小写转换 |
#include <ctype.h>
int is_identifier_char(int ch)
{
return isalnum((unsigned char)ch) || ch == '_';
}errno.h:错误码
很多 C 标准库函数失败时会设置全局错误码 errno。常和 perror、strerror 搭配使用。
#include <errno.h>
#include <stdio.h>
#include <string.h>
FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
fprintf(stderr, "open failed: %s\n", strerror(errno));
}time.h:时间和日期
常用能力:
| 函数/类型 | 用途 |
|---|---|
time_t | 日历时间 |
time | 获取当前时间 |
localtime / gmtime | 转本地时间/UTC |
strftime | 格式化时间 |
clock | 粗略 CPU 时间测量 |
格式化当前时间:
#include <stdio.h>
#include <time.h>
time_t now = time(NULL);
struct tm *local = localtime(&now);
char buffer[64];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local);
printf("%s\n", buffer);math.h:数学计算
常用能力:
| 函数 | 用途 |
|---|---|
fabs | 浮点绝对值 |
sqrt | 平方根 |
pow | 幂 |
sin / cos / tan | 三角函数 |
floor / ceil / round | 向下/向上/四舍五入 |
fmod | 浮点取余 |
#include <math.h>
double distance(double x1, double y1, double x2, double y2)
{
double dx = x1 - x2;
double dy = y1 - y2;
return sqrt(dx * dx + dy * dy);
}assert.h:断言
断言用于检查“理论上不应该失败”的程序内部条件。
#include <assert.h>
void set_age(int age)
{
assert(age >= 0);
}stdint.h / stddef.h / stdbool.h:基础类型
常用类型:
| 头文件 | 类型/宏 | 用途 |
|---|---|---|
stdint.h | int32_t、uint64_t | 固定宽度整数 |
stddef.h | size_t、NULL、offsetof | 大小、空指针、成员偏移 |
stdbool.h | bool、true、false | C99 布尔类型 |
#include <stdint.h>
#include <stdbool.h>
uint32_t user_id = 1001;
bool enabled = true;stdarg.h:可变参数
用于实现类似 printf 的可变参数函数。
#include <stdarg.h>
#include <stdio.h>
void log_message(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}signal.h:简单信号处理
用于处理 SIGINT、SIGTERM 等信号。标准 C 的能力有限,复杂服务程序通常需要平台 API。
#include <signal.h>
#include <stdio.h>
void handle_signal(int sig)
{
printf("signal: %d\n", sig);
}
signal(SIGINT, handle_signal);业务开发常见组合
配置文件读取
stdio.h:fopen、fgetsstring.h:strchr、strcmp、strlenstdlib.h:strtol、strtodctype.h:跳过空白字符
日志输出
stdio.h:fprintf、snprintftime.h:time、strftimestdarg.h:封装可变参数日志函数
数据处理
stdlib.h:qsort、bsearchstring.h:memcpy、memmove、memcmpstdint.h:固定宽度整数,便于二进制协议处理
错误处理
errno.h:读取错误码string.h:strerrorstdio.h:perror、fprintf(stderr, ...)
最佳代码实践
- 文件、内存、字符串都要先考虑边界:缓冲区大小、返回值、失败路径。
- 字符串转数字优先用
strtol/strtod,不要用无法判断错误的atoi。 - 输出到固定缓冲区优先用
snprintf,不要用sprintf。 memcpy只用于不重叠内存;内存区域可能重叠时用memmove。- 比较函数不要写
return lhs - rhs;,可能整数溢出。 - 使用
ctype.h函数时,把char转成unsigned char再传入。 - 跨平台业务代码避免依赖
system("pause")、strrev、itoa这类非标准函数。
错误用法
char dst[4];
strcpy(dst, "hello"); // 错误:缓冲区不够char buffer[16];
sprintf(buffer, "%s", long_text); // 错误风险:可能写爆 bufferprintf("%d\n", strlen("abc")); // 错误倾向:strlen 返回 size_t,应使用 %zuint compare_int_bad(const void *a, const void *b)
{
return *(const int*)a - *(const int*)b; // 错误风险:可能溢出
}char ch = -1;
if (isdigit(ch)) { // 错误风险:负 char 传给 ctype 函数可能未定义
}注意事项
- C 标准库能完成不少业务基础工作,但没有高级容器、HTTP、JSON、线程池、数据库客户端等能力;这些通常需要第三方库或平台 API。
strtok会修改原字符串,且传统strtok有全局状态,多线程场景要谨慎。localtime、gmtime返回静态存储区指针,多线程场景使用平台提供的安全版本。- C++ 项目中使用 C 标准库时,优先包含
<cstdio>、<cstdlib>、<cstring>、<cmath>等 C++ 风格头文件。