Bootstrap

C 标准库总结

C 标准库总览表

下表按功能类别列出了常用头文件。您可根据需求先在表中找到相应功能与头文件,再查看后续的示例与注意事项。

功能类别头文件主要功能/特性常用函数例子
标准输入输出(I/O)<stdio.h>标准输入输出、文件操作printf(), scanf(), fopen(), fgets(), fprintf()
内存与工具函数<stdlib.h>内存分配、程序退出、转换函数、随机数malloc(), free(), exit(), atoi(), strtol(), rand()
字符串与内存操作<string.h>字符串处理、内存块操作strlen(), strcpy(), strncpy(), strcat(), memcmp()
字符分类与转换<ctype.h>字符分类检查与大小写转换isalpha(), isdigit(), isspace(), toupper(), tolower()
时间与日期<time.h>获取系统时间、格式化日期时间、计时time(), localtime(), gmtime(), strftime(), clock()
数学函数<math.h>基本数学运算与函数sin(), cos(), tan(), log(), sqrt(), pow()
错误处理与断言<errno.h>, <assert.h>错误码 errno 与断言调试辅助assert(), perror(), strerror()
类型限制与定长整数<limits.h>, <float.h>, <stdint.h>, <inttypes.h>基本类型范围、浮点限制、定长整数类型及格式化宏INT_MAX, DBL_MAX, int32_t, PRId32
布尔类型支持<stdbool.h>布尔类型 bool, true, false(无函数,仅宏与类型定义)
本地化与宽字符支持<locale.h>, <wchar.h>, <wctype.h>, <uchar.h>地域化设置、宽字符与Unicode支持setlocale(), wprintf(), iswalpha()
信号与非局部跳转<signal.h>, <setjmp.h>信号处理与非局部跳转signal(), raise(), setjmp(), longjmp()
可变参数处理<stdarg.h>可变参数函数支持(如实现printf风格函数)va_list, va_start(), va_arg(), va_end()
原子与多线程(C11)<stdatomic.h>, <threads.h>原子操作、多线程API(C11引入)atomic_store(), thrd_create(), thrd_join(), mtx_lock()
对齐与不可返回函数(C11)<stdalign.h>, <stdnoreturn.h>内存对齐与不可返回函数说明alignof(), _Noreturn关键字
复数与特殊数学(C99)<complex.h>, <fenv.h>, <tgmath.h>复数操作、浮点环境控制、类型泛型数学宏cabs(), fesetround(), 使用tgm宏自动选择数学函数版本

深入示例与注意事项

1. 标准输入输出(<stdio.h>)

典型场景:文件读写、标准输入输出、格式化打印。

示例:将文本写入文件并读出

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp = fopen("data.txt", "w");
    if (!fp) {
        perror("Open for writing failed");
        return EXIT_FAILURE;
    }
    fprintf(fp, "Hello, world!\n");
    fclose(fp);

    fp = fopen("data.txt", "r");
    if (!fp) {
        perror("Open for reading failed");
        return EXIT_FAILURE;
    }
    char buf[100];
    if (fgets(buf, sizeof(buf), fp)) {
        printf("Read: %s", buf);
    }
    fclose(fp);
    return 0;
}

注意事项

  • 使用 fgets() 而非 gets() 来避免缓冲区溢出。
  • 打开文件后需记得 fclose()
  • 格式化输出时,确保格式说明符匹配传入参数类型。

2. 内存分配与通用工具(<stdlib.h>)

典型场景:动态分配数组,转换字符串为数值,生成随机数。

示例:动态分配与随机数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int *arr = malloc(5 * sizeof(int));
    if (!arr) {
        fprintf(stderr, "malloc failed\n");
        return EXIT_FAILURE;
    }
    for (int i = 0; i < 5; i++) arr[i] = i*i;

    srand((unsigned)time(NULL));
    int r = rand() % 100;
    printf("Random: %d\n", r);

    free(arr);
    return 0;
}

注意事项

  • 每次 malloc()/calloc() 分配的内存必须 free()
  • 对转换类函数如 atoi(),若需严谨错误检测,用 strtol()替代。

3. 字符串与内存(<string.h>)

典型场景:字符串复制、拼接、比较,内存块拷贝清零。

示例:安全字符串拷贝与搜索

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello C";
    char dest[20];

    // 使用strncpy以避免溢出,并手动添加'\0'
    strncpy(dest, src, sizeof(dest)-1);
    dest[sizeof(dest)-1] = '\0';
    printf("Copied: %s\n", dest);

    char *pos = strstr(dest, "C");
    if (pos) {
        printf("Found 'C' at index %ld\n", pos - dest);
    }

    return 0;
}

注意事项

  • 始终确保目标缓冲区足够大,并在需要时手动添加终止符'\0'
  • 对二进制数据用 memcpy()/memmove() 而非 strcpy()

4. 字符类型检查(<ctype.h>)

典型场景:判断字符是否字母、数字,转换大小写。

示例

#include <stdio.h>
#include <ctype.h>

int main() {
    char c = '9';
    if (isdigit((unsigned char)c)) {
        printf("%c is a digit\n", c);
    }

    c = 'a';
    printf("Uppercase: %c\n", toupper((unsigned char)c));
    return 0;
}

注意事项

  • char转为unsigned char传给isxxx()函数,以避免负值出现未定义行为。

5. 时间与日期(<time.h>)

典型场景:获取当前时间、格式化输出、统计运行时间。

示例:打印当前时间

#include <stdio.h>
#include <time.h>

int main() {
    time_t now = time(NULL);
    struct tm *lt = localtime(&now);
    char buf[100];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt);
    printf("Local time: %s\n", buf);
    return 0;
}

注意事项

  • localtime()返回静态存储区指针,下次调用会覆盖,使用前若需保存结果请复制数据。
  • C标准不提供复杂日期处理,需要自行处理闰年等逻辑或使用第三方库。

6. 数学函数(<math.h>)

典型场景:计算三角函数、对数、指数、开方、幂。

示例:计算圆面积

#include <stdio.h>
#include <math.h>

int main() {
    double radius = 2.0;
    double area = M_PI * pow(radius, 2);
    printf("Area: %f\n", area);
    return 0;
}

注意事项

  • 确保定义 _USE_MATH_DEFINES(在某些编译器中)或自己定义PI来使用M_PI
  • 浮点精度有限,比较时需考虑误差。

7. 错误处理与断言(<errno.h>, <assert.h>)

典型场景:检查函数调用失败原因,调试时检查不变条件。

示例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

int main() {
    FILE *fp = fopen("nonexist.txt", "r");
    if (!fp) {
        fprintf(stderr, "Error: %s\n", strerror(errno));
    }

    int x = 10;
    assert(x == 10); // 若不满足则程序中断
    return 0;
}

注意事项

  • 发布版本可#define NDEBUG禁用assert()
  • errno只在特定函数失败时有意义。

8. 类型限制与定长整数

典型场景:确定整数范围、使用定长类型确保跨平台一致性。

示例

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>

int main() {
    printf("INT_MAX = %d\n", INT_MAX);
    int32_t val = 12345;
    printf("val = %" PRId32 "\n", val);
    return 0;
}

注意事项

  • 使用<stdint.h>确保整数位宽固定。
  • <inttypes.h>提供打印定长整数的安全格式化宏。

9. 布尔与本地化支持(<stdbool.h>, <locale.h>等)

典型场景:使用布尔类型代码更清晰,或设置地域化格式。

示例(布尔类型):

#include <stdio.h>
#include <stdbool.h>

int main() {
    bool flag = true;
    if (flag) {
        printf("Flag is true\n");
    }
    return 0;
}

示例(locale):

#include <stdio.h>
#include <locale.h>

int main() {
    setlocale(LC_ALL, "");
    // 输出可能在特定语言环境下启用特殊格式
    printf("%'.2f\n", 1234567.89); 
    return 0;
}

注意事项

  • 地域化支持依赖系统环境,setlocale()设置地区后相关函数的行为(如数字分组符)才会改变。
  • <wchar.h><wctype.h> 用于宽字符处理,多语言字符需使用Unicode知识。

10. 信号处理与非局部跳转(<signal.h>, <setjmp.h>)

典型场景:捕捉Ctrl+C中断或在深层函数中发生错误时跳回上层函数。

示例(signal):

#include <stdio.h>
#include <signal.h>

void handler(int sig) {
    printf("Caught signal %d\n", sig);
}

int main() {
    signal(SIGINT, handler);
    while (1) {
        // 按 Ctrl+C 看效果
    }
    return 0;
}

注意事项

  • 信号处理函数中只能调用异步信号安全函数。
  • 非局部跳转setjmp()longjmp()不推荐滥用,影响代码可读性和可维护性。

11. 可变参数(<stdarg.h>)

典型场景:实现自定义变参函数,比如日志函数。

示例

#include <stdio.h>
#include <stdarg.h>

void print_numbers(int count, ...) {
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        int num = va_arg(args, int);
        printf("%d ", num);
    }
    va_end(args);
    printf("\n");
}

int main() {
    print_numbers(3, 10, 20, 30);
    return 0;
}

注意事项

  • 需约定参数类型顺序,无类型安全检查。
  • 如果能固定参数数量或使用变参安全框架更好。

12. 原子与多线程(C11)(<stdatomic.h>, <threads.h>)

典型场景:使用标准原子操作、跨平台基础多线程。

示例(简单线程):

#include <stdio.h>
#include <threads.h>

int func(void *arg) {
    printf("In thread: %d\n", *(int*)arg);
    return 0;
}

int main() {
    int val = 42;
    thrd_t t;
    if (thrd_create(&t, func, &val) == thrd_success) {
        thrd_join(t, NULL);
    }
    return 0;
}

注意事项

  • 功能不如Java Thread和并发库丰富。仅适合简单多线程场景。
  • <stdatomic.h>提供类似C++原子操作功能,保证数据访问的原子性。

13. 对齐与不可返回函数(C11)

典型场景:特殊场景下需指定类型对齐或标识函数不返回。

示例

#include <stdalign.h>
#include <stdnoreturn.h>
#include <stdio.h>
#include <stdlib.h>

_Noreturn void fail(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    exit(EXIT_FAILURE);
}

int main() {
    struct {
        alignas(16) int x; 
    } s;

    s.x = 10;
    // fail("This function never returns");
    return 0;
}

注意事项

  • 大部分日常开发无需显式使用对齐特性。
  • _Noreturn函数不能返回,否则未定义行为。

14. 复数与特殊数学(C99)(<complex.h>, <fenv.h>, <tgmath.h>)

典型场景:科学计算,处理复数或控制浮点环境(如舍入方式)。

示例(复数):

#include <stdio.h>
#include <complex.h>
#include <math.h>

int main() {
    double complex z = 1.0 + 2.0*I;
    double r = cabs(z);
    printf("|1+2i| = %f\n", r);
    return 0;
}

注意事项

  • 日常通用开发中很少使用。
  • <fenv.h>控制浮点异常、舍入等,需要特殊场景。

总结与建议

  • 安全性与内存管理:C无自动内存管理和越界检查,需格外小心。
  • 错误处理:充分利用errnoperror(), 并在调试期使用assert()确保逻辑正确。
  • 国际化与复杂场景:对于多语言文本、时区、本地化格式等,高级功能需<locale.h><wchar.h><wctype.h>支持,且平台相关性更高。
  • 扩展与第三方库:C标准库提供基础功能,如需高级数据结构、网络编程、图形界面等,需第三方库或操作系统API。
;