C 语言常用库

内容简介

C 标准库不是“函数大全”,而是 C 业务开发的基础工具箱。它覆盖输入输出、文件、字符串、内存、动态分配、排序查找、数学计算、时间、错误处理、断言、字符处理、固定宽度整数等能力。

学习顺序建议:

  1. stdio.h:输入输出和文件。
  2. stdlib.h:内存、转换、排序、查找、程序控制。
  3. string.h:字符串和内存块操作。
  4. ctype.h:字符分类和大小写转换。
  5. errno.h:错误码处理。
  6. time.h:时间和日期。
  7. stdint.h / stdbool.h / stddef.h:更清晰的基础类型。
  8. 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。常和 perrorstrerror 搭配使用。

#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.hint32_tuint64_t固定宽度整数
stddef.hsize_tNULLoffsetof大小、空指针、成员偏移
stdbool.hbooltruefalseC99 布尔类型
#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:简单信号处理

用于处理 SIGINTSIGTERM 等信号。标准 C 的能力有限,复杂服务程序通常需要平台 API。

#include <signal.h>
#include <stdio.h>
 
void handle_signal(int sig)
{
    printf("signal: %d\n", sig);
}
 
signal(SIGINT, handle_signal);

业务开发常见组合

配置文件读取

  • stdio.hfopenfgets
  • string.hstrchrstrcmpstrlen
  • stdlib.hstrtolstrtod
  • ctype.h:跳过空白字符

日志输出

  • stdio.hfprintfsnprintf
  • time.htimestrftime
  • stdarg.h:封装可变参数日志函数

数据处理

  • stdlib.hqsortbsearch
  • string.hmemcpymemmovememcmp
  • stdint.h:固定宽度整数,便于二进制协议处理

错误处理

  • errno.h:读取错误码
  • string.hstrerror
  • stdio.hperrorfprintf(stderr, ...)

最佳代码实践

  • 文件、内存、字符串都要先考虑边界:缓冲区大小、返回值、失败路径。
  • 字符串转数字优先用 strtol / strtod,不要用无法判断错误的 atoi
  • 输出到固定缓冲区优先用 snprintf,不要用 sprintf
  • memcpy 只用于不重叠内存;内存区域可能重叠时用 memmove
  • 比较函数不要写 return lhs - rhs;,可能整数溢出。
  • 使用 ctype.h 函数时,把 char 转成 unsigned char 再传入。
  • 跨平台业务代码避免依赖 system("pause")strrevitoa 这类非标准函数。

错误用法

char dst[4];
strcpy(dst, "hello");  // 错误:缓冲区不够
char buffer[16];
sprintf(buffer, "%s", long_text);  // 错误风险:可能写爆 buffer
printf("%d\n", strlen("abc"));  // 错误倾向:strlen 返回 size_t,应使用 %zu
int 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 有全局状态,多线程场景要谨慎。
  • localtimegmtime 返回静态存储区指针,多线程场景使用平台提供的安全版本。
  • C++ 项目中使用 C 标准库时,优先包含 <cstdio><cstdlib><cstring><cmath> 等 C++ 风格头文件。

相关:CandCPP文件操作CandCpp函数C语言MallocFreeCandCpp指针