标准库
C 标准库提供了大量实用函数,涵盖输入输出、字符串处理、数学计算、内存管理等方面。熟练使用标准库可以大大提高开发效率。
输入输出库 stdio.h
文件操作
FILE* fopen(const char* filename, const char* mode); // 打开文件
int fclose(FILE* stream); // 关闭文件
int fflush(FILE* stream); // 刷新缓冲区
格式化输入输出
int printf(const char* format, ...); // 格式化输出到 stdout
int fprintf(FILE* stream, const char* format, ...); // 格式化输出到文件
int sprintf(char* str, const char* format, ...); // 格式化输出到字符串
int snprintf(char* str, size_t size, const char* format, ...); // 安全版本
int scanf(const char* format, ...); // 从 stdin 格式化输入
int fscanf(FILE* stream, const char* format, ...); // 从文件格式化输入
int sscanf(const char* str, const char* format, ...); // 从字符串格式化输入
格式说明符:
| 格式符 | 说明 |
|---|---|
%d | 有符号十进制整数 |
%u | 无符号十进制整数 |
%x | 十六进制(小写) |
%X | 十六进制(大写) |
%o | 八进制 |
%f | 浮点数 |
%e | 科学计数法 |
%c | 字符 |
%s | 字符串 |
%p | 指针 |
%zu | size_t |
%% | 百分号 |
字符输入输出
int fgetc(FILE* stream); // 从文件读取一个字符
int fputc(int ch, FILE* stream); // 向文件写入一个字符
char* fgets(char* str, int n, FILE* stream); // 从文件读取一行
int fputs(const char* str, FILE* stream); // 向文件写入字符串
二进制输入输出
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
文件定位
int fseek(FILE* stream, long offset, int origin); // 设置文件位置
long ftell(FILE* stream); // 获取文件位置
void rewind(FILE* stream); // 重置到开头
int feof(FILE* stream); // 检查是否到达末尾
标准库 stdlib.h
内存管理
void* malloc(size_t size); // 分配内存
void* calloc(size_t n, size_t size); // 分配并初始化为 0
void* realloc(void* ptr, size_t size); // 重新分配
void free(void* ptr); // 释放内存
字符串转换
int atoi(const char* str); // 字符串转整数
long atol(const char* str); // 字符串转 long
double atof(const char* str); // 字符串转 double
long strtol(const char* str, char** endptr, int base); // 安全版本
double strtod(const char* str, char** endptr); // 安全版本
随机数
int rand(void); // 生成随机数(0 到 RAND_MAX)
void srand(unsigned int seed); // 设置随机种子
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
srand(time(NULL)); // 使用时间作为种子
for (int i = 0; i < 5; i++) {
printf("%d ", rand() % 100); // 0-99 的随机数
}
return 0;
}
程序控制
void exit(int status); // 正常终止程序
void abort(void); // 异常终止程序
int atexit(void (*func)(void)); // 注册退出函数
int system(const char* command); // 执行系统命令
void cleanup(void) {
printf("程序即将退出\n");
}
int main(void) {
atexit(cleanup); // 注册清理函数
printf("程序运行中...\n");
return 0; // 退出时自动调用 cleanup
}
排序与搜索
void qsort(void* base, size_t n, size_t size,
int (*compar)(const void*, const void*));
void* bsearch(const void* key, const void* base, size_t n, size_t size,
int (*compar)(const void*, const void*));
int compare_int(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int main(void) {
int arr[] = {5, 2, 8, 1, 9, 3, 7, 4, 6};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare_int);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
int key = 5;
int* result = bsearch(&key, arr, n, sizeof(int), compare_int);
if (result != NULL) {
printf("找到: %d\n", *result);
}
return 0;
}
整数运算
int abs(int n); // 绝对值
long labs(long n);
div_t div(int numer, int denom); // 除法,返回商和余数
div_t result = div(17, 5);
printf("商: %d, 余数: %d\n", result.quot, result.rem); // 商: 3, 余数: 2
字符串库 string.h
字符串操作
size_t strlen(const char* str); // 字符串长度
char* strcpy(char* dest, const char* src); // 复制字符串
char* strncpy(char* dest, const char* src, size_t n); // 安全复制
char* strcat(char* dest, const char* src); // 连接字符串
char* strncat(char* dest, const char* src, size_t n); // 安全连接
字符串比较
int strcmp(const char* s1, const char* s2); // 比较字符串
int strncmp(const char* s1, const char* s2, size_t n); // 比较前 n 个字符
返回值:小于 0(s1 < s2)、等于 0(相等)、大于 0(s1 > s2)。
字符串搜索
char* strchr(const char* str, int ch); // 查找字符(首次出现)
char* strrchr(const char* str, int ch); // 查找字符(最后出现)
char* strstr(const char* haystack, const char* needle); // 查找子串
char* strtok(char* str, const char* delim); // 分割字符串
char str[] = "Hello, World!";
char* p = strchr(str, 'o'); // "o, World!"
char* q = strstr(str, "World"); // "World!"
char text[] = "apple,banana,orange";
char* token = strtok(text, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
内存操作
void* memcpy(void* dest, const void* src, size_t n); // 内存复制
void* memmove(void* dest, const void* src, size_t n); // 内存移动(允许重叠)
void* memset(void* str, int c, size_t n); // 设置内存
int memcmp(const void* s1, const void* s2, size_t n); // 内存比较
void* memchr(const void* str, int c, size_t n); // 内存搜索
int arr[10];
memset(arr, 0, sizeof(arr)); // 全部设置为 0
int src[] = {1, 2, 3, 4, 5};
int dst[5];
memcpy(dst, src, sizeof(src)); // 复制数组
字符处理库 ctype.h
字符分类
int isalpha(int c); // 是否字母
int isdigit(int c); // 是否数字
int isalnum(int c); // 是否字母或数字
int isspace(int c); // 是否空白字符
int isupper(int c); // 是否大写字母
int islower(int c); // 是否小写字母
int ispunct(int c); // 是否标点符号
int isprint(int c); // 是否可打印字符
int iscntrl(int c); // 是否控制字符
int isxdigit(int c); // 是否十六进制数字
字符转换
int toupper(int c); // 转大写
int tolower(int c); // 转小写
char str[] = "Hello, World!";
for (int i = 0; str[i] != '\0'; i++) {
str[i] = tolower(str[i]);
}
printf("%s\n", str); // hello, world!
// 统计字母数量
int letters = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (isalpha(str[i])) {
letters++;
}
}
数学库 math.h
基本运算
double fabs(double x); // 绝对值
double fmod(double x, double y); // 取模
double pow(double x, double y); // 幂运算
double sqrt(double x); // 平方根
double cbrt(double x); // 立方根(C99)
取整函数
double ceil(double x); // 向上取整
double floor(double x); // 向下取整
double round(double x); // 四舍五入(C99)
double trunc(double x); // 截断小数(C99)
指数与对数
double exp(double x); // e^x
double log(double x); // 自然对数
double log10(double x); // 以 10 为底的对数
double log2(double x); // 以 2 为底的对数(C99)
三角函数
double sin(double x); // 正弦
double cos(double x); // 余弦
double tan(double x); // 正切
double asin(double x); // 反正弦
double acos(double x); // 反余弦
double atan(double x); // 反正切
double atan2(double y, double x); // 双参数反正切
其他函数
double hypot(double x, double y); // 斜边长度 sqrt(x² + y²)
double ldexp(double x, int exp); // x * 2^exp
double frexp(double x, int* exp); // 分解为尾数和指数
#include <math.h>
#include <stdio.h>
int main(void) {
printf("sqrt(16) = %f\n", sqrt(16)); // 4.0
printf("pow(2, 10) = %f\n", pow(2, 10)); // 1024.0
printf("ceil(3.2) = %f\n", ceil(3.2)); // 4.0
printf("floor(3.8) = %f\n", floor(3.8)); // 3.0
printf("round(3.5) = %f\n", round(3.5)); // 4.0
printf("fabs(-5.5) = %f\n", fabs(-5.5)); // 5.5
double angle = M_PI / 4; // 45 度
printf("sin(45°) = %f\n", sin(angle)); // 0.707...
printf("cos(45°) = %f\n", cos(angle)); // 0.707...
return 0;
}
时间库 time.h
时间类型
time_t // 日历时间类型
struct tm // 分解的时间结构
clock_t // 处理器时间类型
struct tm 成员:
| 成员 | 说明 | 范围 |
|---|---|---|
tm_sec | 秒 | 0-60 |
tm_min | 分 | 0-59 |
tm_hour | 时 | 0-23 |
tm_mday | 日 | 1-31 |
tm_mon | 月 | 0-11 |
tm_year | 年(从 1900 起) | |
tm_wday | 星期 | 0-6 |
tm_yday | 年内天数 | 0-365 |
tm_isdst | 夏令时标志 |
时间函数
time_t time(time_t* timer); // 获取当前时间
struct tm* localtime(const time_t* timer); // 转换为本地时间
struct tm* gmtime(const time_t* timer); // 转换为 UTC 时间
char* ctime(const time_t* timer); // 时间转字符串
char* asctime(const struct tm* tm); // tm 转字符串
time_t mktime(struct tm* tm); // tm 转 time_t
size_t strftime(char* str, size_t max, const char* format, const struct tm* tm); // 格式化时间
#include <time.h>
#include <stdio.h>
int main(void) {
time_t now = time(NULL);
printf("当前时间戳: %ld\n", now);
char* time_str = ctime(&now);
printf("ctime: %s", time_str);
struct tm* local = localtime(&now);
printf("年: %d\n", local->tm_year + 1900);
printf("月: %d\n", local->tm_mon + 1);
printf("日: %d\n", local->tm_mday);
char buffer[100];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local);
printf("格式化: %s\n", buffer);
return 0;
}
计时
clock_t clock(void); // 获取处理器时间
double difftime(time_t end, time_t start); // 计算时间差
#include <time.h>
#include <stdio.h>
int main(void) {
clock_t start = clock();
// 执行一些操作
volatile long sum = 0;
for (long i = 0; i < 100000000; i++) {
sum += i;
}
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("耗时: %.3f 秒\n", elapsed);
return 0;
}
断言库 assert.h
void assert(int expression); // 运行时断言
#include <assert.h>
void process(int* ptr, int size) {
assert(ptr != NULL);
assert(size > 0);
for (int i = 0; i < size; i++) {
ptr[i] = i * 2;
}
}
定义 NDEBUG 可以禁用断言:
#define NDEBUG
#include <assert.h> // 断言被禁用
布尔类型 stdbool.h(C99)
#define bool _Bool
#define true 1
#define false 0
#define __bool_true_false_are_defined 1
#include <stdbool.h>
bool is_even(int n) {
return n % 2 == 0;
}
int main(void) {
bool flag = true;
if (flag) {
printf("flag is true\n");
}
return 0;
}
固定宽度整数 stdint.h(C99)
int8_t // 8 位有符号整数
int16_t // 16 位有符号整数
int32_t // 32 位有符号整数
int64_t // 64 位有符号整数
uint8_t // 8 位无符号整数
uint16_t // 16 位无符号整数
uint32_t // 32 位无符号整数
uint64_t // 64 位无符号整数
intptr_t // 能容纳指针的整数
uintptr_t // 能容纳指针的无符号整数
#include <stdint.h>
#include <stdio.h>
int main(void) {
int32_t value = 2147483647;
uint64_t big_value = 18446744073709551615ULL;
printf("int32_t 大小: %zu\n", sizeof(int32_t)); // 4
printf("int64_t 大小: %zu\n", sizeof(int64_t)); // 8
return 0;
}
C23 新标准库特性
C23 标准引入了许多新的库函数和类型,扩展了 C 标准库的能力。
新的安全函数
memset_explicit:
memset_explicit 是 memset 的安全版本,保证内存被实际清除,不会被编译器优化掉:
#include <string.h>
void secure_clear(void) {
char password[100] = "my_secret_password";
// 使用 memset 可能被编译器优化掉
// memset(password, 0, sizeof(password));
// memset_explicit 保证内存被清除
memset_explicit(password, 0, sizeof(password));
}
这在处理敏感数据(如密码、密钥)时特别重要。
POSIX 函数标准化
C23 将一些常用的 POSIX 函数纳入标准库:
strdup - 复制字符串:
#include <string.h>
char* strdup(const char* str);
// 使用示例
char* copy = strdup("Hello, World!");
if (copy != NULL) {
printf("%s\n", copy);
free(copy); // 记得释放
}
strndup - 复制指定长度字符串:
#include <string.h>
char* strndup(const char* str, size_t n);
// 使用示例
const char* text = "Hello, World!";
char* prefix = strndup(text, 5); // 只复制前 5 个字符
if (prefix != NULL) {
printf("%s\n", prefix); // "Hello"
free(prefix);
}
memccpy - 内存复制直到指定字符:
#include <string.h>
void* memccpy(void* dest, const void* src, int c, size_t n);
// 使用示例
char dest[100];
const char* src = "Hello,World!";
void* result = memccpy(dest, src, ',', 100);
// dest 现在包含 "Hello,"
// result 指向 dest 中的 ',' 之后的位置
线程安全的 localtime_r 和 gmtime_r:
#include <time.h>
struct tm* localtime_r(const time_t* timer, struct tm* result);
struct tm* gmtime_r(const time_t* timer, struct tm* result);
// 使用示例
time_t now = time(NULL);
struct tm tm_result;
if (localtime_r(&now, &tm_result) != NULL) {
printf("年: %d\n", tm_result.tm_year + 1900);
printf("月: %d\n", tm_result.tm_mon + 1);
printf("日: %d\n", tm_result.tm_mday);
}
这些 _r 后缀的函数是线程安全的,使用调用者提供的缓冲区,而不是静态缓冲区。
UTF-8 支持(<uchar.h>)
C23 增强了对 UTF-8 的支持:
char8_t 类型:
#include <uchar.h>
char8_t utf8_char = u8'A'; // UTF-8 字符
const char8_t* utf8_str = u8"你好"; // UTF-8 字符串
mbrtoc8 和 c8rtomb:
#include <uchar.h>
#include <wchar.h>
#include <string.h>
// 多字节转 UTF-8
size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps);
// UTF-8 转多字节
size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps);
// 使用示例
const char* multibyte_str = "你好";
char8_t utf8_buf[10];
mbstate_t state = {0};
size_t len = mbrtoc8(utf8_buf, multibyte_str, strlen(multibyte_str), &state);
新的格式化功能
二进制格式化(%b):
#include <stdio.h>
int main(void) {
int value = 42;
// C23 支持二进制输出
printf("二进制: %b\n", value); // 输出: 101010
printf("8位二进制: %08b\n", value); // 输出: 00101010
return 0;
}
扩展的长度修饰符:
#include <stdio.h>
#include <stdint.h>
int main(void) {
// 用于 _BitInt 类型(如果支持)
_BitInt(16) value = 12345;
// printf("值: %w16d\n", value); // wN 修饰符
return 0;
}
新的宏和常量
整数宽度宏:
#include <limits.h>
// C23 新增的位宽宏
printf("int 位宽: %d\n", INT_WIDTH);
printf("long 位宽: %d\n", LONG_WIDTH);
printf("long long 位宽: %d\n", LLONG_WIDTH);
浮点数限制宏:
#include <float.h>
// C23 新增的浮点数特性宏
printf("float 最小指数: %d\n", FLT_MIN_10_EXP);
printf("double 十进制数字: %d\n", DBL_DECIMAL_DIG);
十进制浮点类型(可选)
C23 支持十进制浮点类型(_Decimal32、_Decimal64、_Decimal128),这在金融计算中非常有用:
#include <decimal.h> // 可选头文件
_Decimal32 price = 19.99df; // 十进制32位
_Decimal64 amount = 1000.00dd; // 十进制64位
_Decimal128 total = 12345.67dl; // 十进制128位
// 十进制浮点避免了二进制浮点的精度问题
_Decimal32 a = 0.1df;
_Decimal32 b = 0.2df;
_Decimal32 c = a + b; // 精确等于 0.3df,没有精度误差
库版本测试宏
C23 提供了测试标准库版本的宏:
#include <stdio.h>
int main(void) {
// 检查 C 标准版本
#ifdef __STDC_VERSION__
printf("C 标准版本: %ldL\n", __STDC_VERSION__);
#if __STDC_VERSION__ >= 202311L
printf("支持 C23 特性\n");
#elif __STDC_VERSION__ >= 201112L
printf("支持 C11 特性\n");
#endif
#endif
// 检查可选特性
#ifdef __STDC_IEC_60559_BFP__
printf("支持 IEEE 754 二进制浮点\n");
#endif
#ifdef __STDC_IEC_60559_DFP__
printf("支持 IEEE 754 十进制浮点\n");
#endif
return 0;
}
使用 C23 标准库的建议
- 使用 memset_explicit 清除敏感数据:确保编译器不会优化掉安全操作
- 使用线程安全版本:
localtime_r替代localtime,gmtime_r替代gmtime - 使用 strdup 简化字符串复制:比手动分配+复制更简洁
- 使用 char8_t 处理 UTF-8:类型更明确,避免与普通 char 混淆
// 现代 C23 风格代码示例
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <uchar.h>
int main(void) {
// 安全清除敏感数据
char password[] = "secret123";
memset_explicit(password, 0, sizeof(password));
// 使用 strdup
char* greeting = strdup("Hello, C23!");
if (greeting != NULL) {
printf("%s\n", greeting);
free(greeting);
}
// 线程安全的时间处理
time_t now = time(NULL);
struct tm tm_buf;
if (localtime_r(&now, &tm_buf) != NULL) {
printf("当前年份: %d\n", tm_buf.tm_year + 1900);
}
return 0;
}
位操作库 stdbit.h(C23)
C23 引入了全新的 <stdbit.h> 头文件,提供标准化的位操作函数。这些函数以前需要手动实现或使用编译器内置函数,现在成为标准库的一部分。
位计数函数
前导零/一计数:
#include <stdbit.h>
#include <stdio.h>
int main(void) {
unsigned int value = 0x00F00000;
// 计算前导零数量(从最高位开始)
unsigned int lz = stdc_leading_zeros(value);
printf("前导零数量: %u\n", lz); // 取决于 unsigned int 的位数
// 计算前导一数量
unsigned int ones_value = 0xFF000000;
unsigned int lo = stdc_leading_ones(ones_value);
printf("前导一数量: %u\n", lo);
return 0;
}
尾随零/一计数:
#include <stdbit.h>
unsigned int value = 0x0000F000;
// 计算尾随零数量(从最低位开始)
unsigned int tz = stdc_trailing_zeros(value);
printf("尾随零数量: %u\n", tz); // 12(二进制末尾有12个0)
// 计算尾随一数量
unsigned int ones = 0x0000000F;
unsigned int to = stdc_trailing_ones(ones);
printf("尾随一数量: %u\n", to); // 4
统计零/一总数:
#include <stdbit.h>
unsigned int value = 0b10110100;
// 统计零的位数
unsigned int zeros = stdc_count_zeros(value);
printf("零的位数: %u\n", zeros); // 5
// 统计一的位数(等同于 popcount)
unsigned int ones = stdc_count_ones(value);
printf("一的位数: %u\n", ones); // 4
位查找函数
#include <stdbit.h>
unsigned int value = 0b00101000;
// 查找第一个前导零的位置
unsigned int flz = stdc_first_leading_zero(value);
// 查找第一个前导一的位置
unsigned int flo = stdc_first_leading_one(value);
// 查找第一个尾随零的位置
unsigned int ftz = stdc_first_trailing_zero(value);
// 查找第一个尾随一的位置
unsigned int fto = stdc_first_trailing_one(value);
printf("第一个尾随一的位置: %u\n", fto); // 3(从0开始计数)
单位检测与位宽
#include <stdbit.h>
unsigned int value = 0b00010000;
// 检测是否只有一个位为1(是否是2的幂)
bool single = stdc_has_single_bit(value);
printf("是否只有一个位为1: %s\n", single ? "是" : "否"); // 是
// 计算表示该值所需的最小位数
unsigned int width = stdc_bit_width(255);
printf("表示255需要 %u 位\n", width); // 8
// 计算不大于该值的最大2的幂
unsigned int floor_val = stdc_bit_floor(100);
printf("不大于100的最大2的幂: %u\n", floor_val); // 64
// 计算不小于该值的最小2的幂
unsigned int ceil_val = stdc_bit_ceil(100);
printf("不小于100的最小2的幂: %u\n", ceil_val); // 128
位旋转函数
位旋转是一种重要的密码学操作,将值的位循环移动:
#include <stdbit.h>
#include <stdio.h>
int main(void) {
unsigned int value = 0b10110001;
// 左旋:高位移出的位回到低位
unsigned int left_rotated = stdc_rotate_left(value, 3);
// 原始: 10110001
// 左旋3位: 10001101
// 右旋:低位移出的位回到高位
unsigned int right_rotated = stdc_rotate_right(value, 3);
// 原始: 10110001
// 右旋3位: 00110110
printf("原值: %08b\n", value);
printf("左旋3位: %08b\n", left_rotated);
printf("右旋3位: %08b\n", right_rotated);
return 0;
}
位旋转的应用场景:
- 密码学算法(如 SHA、AES)
- 哈希函数
- 随机数生成
- CRC 校验
类型泛型宏
<stdbit.h> 的函数支持多种整数类型:
#include <stdbit.h>
// 这些函数对 unsigned int、unsigned long、unsigned long long 都有效
unsigned int ui = 0xFF00;
unsigned long ul = 0xFF00FF00UL;
unsigned long long ull = 0xFF00FF00FF00FF00ULL;
printf("unsigned int 前导零: %u\n", stdc_leading_zeros(ui));
printf("unsigned long 前导零: %u\n", stdc_leading_zeros(ul));
printf("unsigned long long 前导零: %u\n", stdc_leading_zeros(ull));
实用示例
判断是否是2的幂:
#include <stdbit.h>
#include <stdbool.h>
bool is_power_of_two(unsigned int n) {
return n > 0 && stdc_has_single_bit(n);
}
// 使用示例
printf("16 是2的幂: %s\n", is_power_of_two(16) ? "是" : "否"); // 是
printf("18 是2的幂: %s\n", is_power_of_two(18) ? "是" : "否"); // 否
计算对数(以2为底):
#include <stdbit.h>
// 计算 log2(n),向下取整
unsigned int log2_floor(unsigned int n) {
if (n == 0) return 0; // 特殊情况
return stdc_bit_width(n) - 1;
}
// 计算 log2(n),向上取整
unsigned int log2_ceil(unsigned int n) {
if (n == 0) return 0;
if (stdc_has_single_bit(n)) return stdc_bit_width(n) - 1;
return stdc_bit_width(n);
}
printf("log2(100) 向下: %u\n", log2_floor(100)); // 6
printf("log2(100) 向上: %u\n", log2_ceil(100)); // 7
内存对齐计算:
#include <stdbit.h>
#include <stddef.h>
// 将大小对齐到下一个2的幂边界
size_t align_to_power_of_two(size_t size) {
if (size == 0) return 1;
return stdc_bit_ceil(size);
}
printf("对齐 100 到 2的幂: %zu\n", align_to_power_of_two(100)); // 128
printf("对齐 64 到 2的幂: %zu\n", align_to_power_of_two(64)); // 64
快速位操作工具函数:
#include <stdbit.h>
// 获取最低位的1(提取最低设置位)
unsigned int get_lowest_set_bit(unsigned int n) {
if (n == 0) return 0;
return n & (-n); // 或者使用 1 << stdc_trailing_zeros(n)
}
// 清除最低位的1
unsigned int clear_lowest_set_bit(unsigned int n) {
return n & (n - 1);
}
// 统计1的个数(人口计数)
unsigned int popcount(unsigned int n) {
return stdc_count_ones(n);
}
// 示例
unsigned int val = 0b10110100;
printf("原始: %08b\n", val);
printf("最低位的1: %08b\n", get_lowest_set_bit(val));
printf("清除最低位1后: %08b\n", clear_lowest_set_bit(val));
printf("1的个数: %u\n", popcount(val));
与传统方法的对比
传统方法(手动实现):
// 传统的前导零计算
int count_leading_zeros_manual(unsigned int x) {
if (x == 0) return sizeof(x) * 8;
int n = 0;
unsigned int mask = 1U << (sizeof(x) * 8 - 1);
while ((x & mask) == 0) {
n++;
mask >>= 1;
}
return n;
}
C23 标准方法:
#include <stdbit.h>
unsigned int lz = stdc_leading_zeros(x); // 简洁、高效、可移植
标准库函数的优势:
- 可移植:所有符合 C23 标准的编译器都支持
- 高效:编译器可以使用硬件指令优化(如 x86 的 LZCNT/TZCNT)
- 类型安全:类型泛型设计,支持多种整数类型
- 清晰:语义明确,代码更易读
线程与原子操作库
C11 标准引入了原生的多线程支持,包括 <threads.h> 线程库和 <stdatomic.h> 原子操作库。
头文件概述
| 头文件 | 说明 |
|---|---|
<threads.h> | 线程创建、互斥量、条件变量、线程特定存储 |
<stdatomic.h> | 原子类型、原子操作、内存序 |
基本类型
线程相关类型:
thrd_t // 线程标识符
mtx_t // 互斥量
cnd_t // 条件变量
tss_t // 线程特定存储
once_flag // 一次性调用标志
原子类型:
atomic_int // 原子整数
atomic_bool // 原子布尔
atomic_flag // 原子标志(保证无锁)
atomic_size_t // 原子 size_t
编译器支持检查
// 检查是否支持线程
#ifdef __STDC_NO_THREADS__
#error "编译器不支持 C11 线程"
#endif
// 检查是否支持原子操作
#ifdef __STDC_NO_ATOMICS__
#error "编译器不支持 C11 原子操作"
#endif
编译选项
# GCC/Clang 需要链接 pthread 库
gcc -std=c11 -pthread program.c -o program
线程和原子操作涉及复杂的并发编程概念,包括数据竞争、同步机制、内存序等。详细的讲解和示例请参考 多线程编程 章节,那里有完整的并发编程教程。
小结
本章介绍了 C 语言常用标准库:
<stdio.h>:输入输出<stdlib.h>:内存管理、随机数、排序搜索<string.h>:字符串和内存操作<ctype.h>:字符分类和转换<math.h>:数学函数<time.h>:时间日期<assert.h>:断言<stdbool.h>:布尔类型<stdint.h>:固定宽度整数<stdbit.h>:位操作(C23)- C23 新特性:memset_explicit、strdup、UTF-8 支持、二进制格式化等
对于 C11 引入的 <threads.h> 线程库和 <stdatomic.h> 原子操作库,请参考 多线程编程 章节获取完整教程。
下一章将学习 多线程编程,深入了解并发编程。