1. C语言常见概念解析
作为一名从大学就开始接触C语言的程序员,我至今记得第一次用printf输出"Hello World"时的兴奋感。C语言就像编程世界的拉丁语——虽然古老,但几乎所有现代编程语言都或多或少受到它的影响。今天我想和大家聊聊那些看似基础却至关重要的C语言概念,这些正是我当年踩过无数坑才真正理解的东西。
在嵌入式开发、操作系统、编译器这些底层领域,C语言依然是无可争议的王者。2023年TIOBE排行榜显示,C语言连续多年保持前两名位置,这充分说明了它的生命力。不同于一些"学了就过时"的技术,掌握C语言的核心概念能让你在技术迭代中始终保持竞争力。
2. 数据类型与变量:程序的基石
2.1 基本数据类型详解
C语言的数据类型系统看似简单,实则暗藏玄机。先看这个典型例子:
c复制int main() {
short a = 32767; // 最大正值
a += 1;
printf("%d", a); // 输出-32768
}
这就是经典的整数溢出问题。理解数据类型的存储方式至关重要:
- char:1字节(-128~127),但经常被误用于处理文本
- short:通常2字节(-32768~32767)
- int:现代系统通常4字节(-2147483648~2147483647)
- long:在32位系统为4字节,64位系统可能8字节
- float/double:遵循IEEE 754标准,但存在精度问题
关键经验:永远不要假设数据类型的大小,使用sizeof运算符获取实际大小。跨平台开发时尤其要注意long类型的变化。
2.2 类型转换的陷阱
隐式类型转换是许多bug的源头。例如:
c复制unsigned int u = 10;
int i = -5;
if (i < u) {
printf("True"); // 实际输出?
}
由于整数提升规则,i会被转换为无符号数,导致比较结果与直觉相反。我建议:
- 避免混合使用有符号和无符号类型
- 显式使用强制类型转换:(int)var
- 启用编译器警告(如gcc的-Wconversion)
3. 指针:C语言的灵魂与噩梦
3.1 指针基础与常见误区
指针是C语言最强大的特性,也是最容易出错的部分。新手常犯的错误包括:
c复制int *p;
*p = 10; // 野指针!
char *str = "literal";
str[0] = 'H'; // 试图修改字符串常量
正确的指针使用姿势:
- 初始化时设为NULL
- 分配内存后检查是否成功
- 使用后及时释放
- 避免返回局部变量的指针
3.2 多级指针与函数指针
理解这些概念是进阶的关键:
c复制// 二级指针示例
void allocate(int **ptr) {
*ptr = malloc(10 * sizeof(int));
}
// 函数指针示例
int (*compare)(int, int) = &my_compare;
qsort(arr, n, sizeof(int), compare);
我在嵌入式项目中常用函数指针实现状态机,这种设计模式非常灵活。
4. 内存管理:从入门到精通
4.1 栈与堆的区别
看这个典型错误:
c复制char *get_string() {
char str[100] = "temp";
return str; // 返回栈内存!
}
理解内存区域至关重要:
- 栈:自动管理,但空间有限
- 堆:手动管理,空间大但容易泄漏
- 静态区:全局变量和static变量
- 代码区:存放程序指令
4.2 动态内存最佳实践
经过多年踩坑,我总结出这些黄金法则:
- malloc/calloc后必须检查返回值
- 使用free后立即将指针置NULL
- 避免"内存饼干"(频繁小内存分配)
- 使用valgrind等工具定期检查
- 实现自己的内存池管理大块内存
c复制// 安全的内存分配模式
int *arr = malloc(size * sizeof(int));
if (!arr) {
perror("malloc failed");
exit(EXIT_FAILURE);
}
5. 文件操作与实战技巧
5.1 文件IO的正确姿势
处理文件时最常见的错误是忽略错误检查:
c复制FILE *fp = fopen("data.txt", "r");
if (!fp) {
perror("Error opening file");
return;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), fp)) {
// 处理每行
}
if (ferror(fp)) {
perror("Read error");
}
fclose(fp);
5.2 二进制文件处理技巧
结构体与文件IO结合时,要注意内存对齐和填充问题:
c复制#pragma pack(push, 1) // 取消对齐填充
struct Record {
int id;
char name[20];
double value;
};
#pragma pack(pop)
// 写入文件
struct Record rec = {1, "test", 3.14};
fwrite(&rec, sizeof(rec), 1, fp);
6. 结构体与联合体高级用法
6.1 匿名结构体的妙用
C11标准引入的匿名结构体可以简化代码:
c复制struct Sensor {
union {
struct { float x, y, z; }; // 匿名结构体
float data[3];
};
};
struct Sensor s;
s.x = 1.0; // 直接访问
s.data[1] = 2.0; // 也可以通过数组访问
6.2 位域的实际应用
在嵌入式系统中,位域可以高效利用内存:
c复制struct Status {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int code : 6;
};
但要注意位域的内存布局是编译器相关的,跨平台时需谨慎。
7. 预处理器与调试技巧
7.1 宏定义的安全用法
避免使用简单的文本替换宏:
c复制// 危险的宏
#define SQUARE(x) x*x
// 调用SQUARE(a+1)会出错
// 安全的写法
#define SQUARE(x) ((x)*(x))
更好的做法是使用static inline函数:
c复制static inline int square(int x) { return x*x; }
7.2 调试打印技巧
我常用的调试宏:
c复制#ifdef DEBUG
#define DBG_PRINT(fmt, ...) \
fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define DBG_PRINT(fmt, ...)
#endif
8. 现代C语言开发实践
8.1 静态分析工具
推荐这些工具提升代码质量:
- clang-tidy:静态检查
- cppcheck:轻量级检查
- splint:老牌但严格
8.2 构建系统选择
根据项目规模选择:
- 小项目:手写Makefile
- 中等项目:CMake
- 大型项目:Autotools
示例CMake配置:
cmake复制cmake_minimum_required(VERSION 3.10)
project(MyProject C)
set(CMAKE_C_STANDARD 11)
add_executable(main main.c utils.c)
9. 常见面试题深度解析
9.1 const与指针的组合
这是面试常考的点:
c复制const int *p1; // 指向常量的指针
int const *p2; // 同上
int * const p3; // 常量指针
const int * const p4; // 指向常量的常量指针
记忆技巧:从右向左读,const修饰它左边的东西。
9.2 大小端判断
经典的面试题解法:
c复制int is_little_endian() {
int x = 1;
return *(char *)&x;
}
在实际网络编程中,必须处理字节序问题,使用htonl/ntohl等函数转换。
10. 性能优化实战经验
10.1 缓存友好的代码
现代CPU的缓存机制对性能影响巨大。例如二维数组的访问:
c复制// 不好的方式:列优先访问
for (int j = 0; j < COLS; j++) {
for (int i = 0; i < ROWS; i++) {
arr[i][j] = 0;
}
}
// 好的方式:行优先访问
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
arr[i][j] = 0;
}
}
10.2 内联函数与宏的选择
对于性能关键路径:
c复制// 头文件中定义
static inline int max(int a, int b) {
return a > b ? a : b;
}
比宏更安全,且现代编译器能很好优化。
11. 嵌入式开发特别注意事项
11.1 volatile关键字
在嵌入式系统中必不可少:
c复制volatile uint32_t *reg = (uint32_t *)0x12345678;
*reg = 0x55; // 确保实际写入硬件寄存器
11.2 位操作技巧
寄存器操作常用技巧:
c复制#define BIT(n) (1U << (n))
// 设置位
reg |= BIT(3);
// 清除位
reg &= ~BIT(3);
// 切换位
reg ^= BIT(3);
12. 安全编程规范
12.1 缓冲区溢出防护
永远不要使用不安全的函数:
c复制// 危险的
char buf[10];
gets(buf);
// 安全的
fgets(buf, sizeof(buf), stdin);
12.2 整数溢出检查
加法运算的安全实现:
c复制int safe_add(int a, int b) {
if ((b > 0 && a > INT_MAX - b) ||
(b < 0 && a < INT_MIN - b)) {
// 处理溢出
}
return a + b;
}
13. 工具链配置建议
13.1 VSCode配置要点
在嵌入式开发中,我的settings.json关键配置:
json复制{
"C_Cpp.default.includePath": [
"${workspaceFolder}/**",
"/path/to/toolchain/include"
],
"C_Cpp.intelliSenseMode": "gcc-x64",
"C_Cpp.errorSquiggles": "Enabled"
}
13.2 编译警告选项
建议至少启用这些gcc警告:
bash复制gcc -Wall -Wextra -Wpedantic -Werror=implicit-function-declaration
14. 项目实战:温度传感器实现
结合热敏电阻的ADC读取示例:
c复制float read_temperature() {
const float BETA = 3950.0;
const float R0 = 10000.0;
const float T0 = 298.15; // 25°C in Kelvin
int adc_value = read_adc();
float R = 10000.0 * (1023.0 / adc_value - 1);
float temp_k = 1.0 / (1.0/BETA * log(R/R0) + 1.0/T0);
return temp_k - 273.15; // 转换为摄氏度
}
15. 递归函数设计模式
以经典的斐波那契数列为例:
c复制// 简单递归(效率低)
int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}
// 带缓存的递归
int fib_memo(int n, int *memo) {
if (memo[n] != -1) return memo[n];
memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo);
return memo[n];
}
16. 多线程编程基础
POSIX线程简单示例:
c复制void *thread_func(void *arg) {
int *val = (int *)arg;
printf("Thread received: %d\n", *val);
return NULL;
}
int main() {
pthread_t tid;
int arg = 42;
pthread_create(&tid, NULL, thread_func, &arg);
pthread_join(tid, NULL);
return 0;
}
注意共享数据的同步问题,必要时使用互斥锁。
17. 代码风格与可维护性
17.1 命名约定建议
我遵循的命名规则:
- 变量:小写加下划线(total_count)
- 常量:全大写(MAX_SIZE)
- 类型:首字母大写(typedef struct Node)
- 函数:动词开头(calculate_total)
17.2 头文件保护
防止重复包含的标准写法:
c复制#ifndef MY_HEADER_H
#define MY_HEADER_H
// 头文件内容
#endif
18. 跨平台开发技巧
18.1 条件编译实战
处理平台差异的典型方式:
c复制#ifdef _WIN32
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#else
#include <unistd.h>
#define SLEEP(ms) usleep((ms)*1000)
#endif
18.2 字节序处理
网络编程中的通用写法:
c复制uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
19. 算法实现示例
快速排序的C语言实现:
c复制void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i+1], &arr[high]);
return i+1;
}
void quick_sort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quick_sort(arr, low, pi-1);
quick_sort(arr, pi+1, high);
}
}
20. 调试与性能分析
20.1 GDB常用命令
我的调试备忘录:
- break:设置断点
- run:启动程序
- next:单步跳过
- step:单步进入
- print:查看变量
- backtrace:查看调用栈
20.2 性能分析工具
Linux下的利器:
- gprof:函数调用分析
- perf:系统级性能分析
- valgrind:内存检查
21. 模块化设计实践
21.1 头文件设计原则
好的头文件应该:
- 只包含必要的声明
- 避免包含其他头文件(使用前置声明)
- 提供清晰的API文档
- 包含保护宏
21.2 接口与实现分离
示例项目结构:
code复制project/
├── include/
│ └── module.h
├── src/
│ └── module.c
└── main.c
22. 日期计算示例
计算一年中的第几天:
c复制int day_of_year(int year, int month, int day) {
static const int days_in_month[] =
{31,28,31,30,31,30,31,31,30,31,30,31};
int total = day;
for (int i = 0; i < month - 1; i++) {
total += days_in_month[i];
}
// 闰年处理
if (month > 2 && (year%4==0 && year%100!=0 || year%400==0)) {
total++;
}
return total;
}
23. 字符串处理进阶
23.1 安全字符串函数
避免使用strcpy/strcat等危险函数:
c复制// 安全的替代方案
strncpy(dest, src, sizeof(dest)-1);
dest[sizeof(dest)-1] = '\0';
snprintf(dest, sizeof(dest), "%s", src);
23.2 常见字符串算法
字符串反转的几种实现:
c复制// 原地反转
void reverse(char *str) {
if (!str) return;
char *end = str + strlen(str) - 1;
while (str < end) {
char temp = *str;
*str++ = *end;
*end-- = temp;
}
}
24. 位操作高级技巧
24.1 位掩码应用
权限系统典型实现:
c复制#define PERM_READ 0x01
#define PERM_WRITE 0x02
#define PERM_EXEC 0x04
int add_permission(int *perms, int perm) {
*perms |= perm;
}
int check_permission(int perms, int perm) {
return (perms & perm) == perm;
}
24.2 位计数算法
高效计算1的位数:
c复制int count_bits(unsigned int n) {
int count = 0;
while (n) {
n &= n - 1;
count++;
}
return count;
}
25. 实用代码片段集锦
25.1 调试宏增强版
c复制#define LOG(fmt, ...) \
do { \
fprintf(stderr, "[%s:%d] " fmt "\n", \
__FILE__, __LINE__, ##__VA_ARGS__); \
} while (0)
#define ASSERT(cond) \
do { \
if (!(cond)) { \
LOG("Assertion failed: %s", #cond); \
abort(); \
} \
} while (0)
25.2 时间测量工具
c复制#include <time.h>
void measure_time() {
clock_t start = clock();
// 被测代码
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("Time: %.6f seconds\n", elapsed);
}
26. 常见编译错误解析
26.1 链接错误处理
典型的未定义引用问题:
- 忘记链接库:添加-l参数(如-lm)
- 函数声明与定义不匹配
- C++调用C函数缺少extern "C"
26.2 预处理错误
宏展开导致的意外结果:
c复制#define SQUARE(x) x*x
int a = SQUARE(1+1); // 展开为1+1*1+1=3
正确的写法应该是:
c复制#define SQUARE(x) ((x)*(x))
27. 嵌入式特定模式
27.1 硬件寄存器访问
标准做法:
c复制#define REG_ADDR 0x12345678
volatile uint32_t *reg = (uint32_t *)REG_ADDR;
*reg |= 0x01; // 设置位
*reg &= ~0x02; // 清除位
27.2 看门狗处理
关键系统的心跳机制:
c复制void watchdog_init() {
// 初始化看门狗
}
void feed_dog() {
// 定期喂狗
}
28. 代码优化实战
28.1 循环优化技巧
减少循环内部计算:
c复制// 优化前
for (int i = 0; i < n; i++) {
arr[i] = x * y + z * w;
}
// 优化后
int temp = x * y + z * w;
for (int i = 0; i < n; i++) {
arr[i] = temp;
}
28.2 分支预测优化
帮助CPU预测分支:
c复制// 可能更高效
if (likely(condition)) {
// 大概率路径
} else {
// 小概率路径
}
29. 多文件项目管理
29.1 Makefile编写要点
基础模板:
makefile复制CC = gcc
CFLAGS = -Wall -O2
LDFLAGS = -lm
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
TARGET = program
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f $(OBJS) $(TARGET)
29.2 头文件依赖管理
自动生成依赖:
makefile复制DEP = $(SRCS:.c=.d)
%.d: %.c
$(CC) -MM $< > $@
-include $(DEP)
30. C语言学习资源推荐
30.1 经典书籍
- 《C程序设计语言》(K&R)
- 《C陷阱与缺陷》
- 《C专家编程》
- 《深入理解C指针》
30.2 在线资源
- cppreference.com
- GCC/GNU文档
- Linux man pages
- Stack Overflow C标签
31. 实际项目经验分享
在开发网吧计费系统时,我深刻体会到:
- 时间处理要使用time_t而非自定义结构
- 金额计算要使用定点数或专门库
- 日志系统要线程安全
- 配置文件解析要严格验证输入
c复制// 时间处理示例
time_t start_time = time(NULL);
// ...
time_t now = time(NULL);
double elapsed = difftime(now, start_time);
32. 编码规范与团队协作
32.1 版本控制实践
Git工作流建议:
- 功能分支开发
- 提交信息规范化
- 定期rebase主分支
- 代码审查必须
32.2 代码审查要点
重点关注:
- 内存管理
- 错误处理
- 边界条件
- 线程安全
- 性能热点
33. 单元测试框架
33.1 简单测试框架
基础测试宏:
c复制#define TEST(cond) \
do { \
if (!(cond)) { \
printf("Test failed at %s:%d\n", __FILE__, __LINE__); \
return 1; \
} \
} while (0)
int test_add() {
TEST(add(1,1) == 2);
TEST(add(-1,1) == 0);
return 0;
}
33.2 使用Check框架
专业级测试:
c复制#include <check.h>
START_TEST(test_addition) {
ck_assert_int_eq(add(1,1), 2);
}
END_TEST
Suite *math_suite() {
Suite *s = suite_create("Math");
TCase *tc = tcase_create("Core");
tcase_add_test(tc, test_addition);
suite_add_tcase(s, tc);
return s;
}
34. 信号处理基础
34.1 基本信号处理
c复制#include <signal.h>
void handler(int sig) {
printf("Received signal %d\n", sig);
}
int main() {
signal(SIGINT, handler);
while(1) pause();
return 0;
}
34.2 信号安全编程
注意:
- 只使用异步信号安全函数
- 避免在信号处理函数中使用锁
- 使用volatile sig_atomic_t标志
35. 网络编程入门
35.1 TCP服务器示例
c复制int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = INADDR_ANY
};
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
listen(sockfd, 5);
while(1) {
int client = accept(sockfd, NULL, NULL);
char buffer[1024];
read(client, buffer, sizeof(buffer));
// 处理请求
close(client);
}
}
35.2 常见网络错误处理
必须检查所有系统调用的返回值:
- socket可能返回-1
- bind可能失败(端口占用)
- read/write可能被信号中断
36. 进程与管道
36.1 进程创建示例
c复制pid_t pid = fork();
if (pid == 0) {
// 子进程
execl("/bin/ls", "ls", "-l", NULL);
} else if (pid > 0) {
// 父进程
wait(NULL);
} else {
perror("fork failed");
}
36.2 管道通信
c复制int pipefd[2];
pipe(pipefd);
if (fork() == 0) {
close(pipefd[0]); // 关闭读端
write(pipefd[1], "hello", 6);
} else {
close(pipefd[1]); // 关闭写端
char buf[6];
read(pipefd[0], buf, sizeof(buf));
}
37. 动态库与静态库
37.1 创建静态库
bash复制gcc -c lib.c -o lib.o
ar rcs lib.a lib.o
37.2 创建动态库
bash复制gcc -fPIC -shared lib.c -o lib.so
使用动态库:
c复制void *handle = dlopen("lib.so", RTLD_LAZY);
void (*func)() = dlsym(handle, "function");
func();
dlclose(handle);
38. 错误处理最佳实践
38.1 统一错误码
定义项目范围的错误码:
c复制typedef enum {
ERR_NONE = 0,
ERR_MEMORY,
ERR_IO,
ERR_INVALID,
// ...
} ErrorCode;
const char *error_string(ErrorCode err) {
static const char *strings[] = {
"No error",
"Memory allocation failed",
"I/O error",
"Invalid argument",
// ...
};
return strings[err];
}
38.2 errno的正确用法
系统调用错误处理:
c复制FILE *fp = fopen("file.txt", "r");
if (!fp) {
perror("fopen failed");
exit(EXIT_FAILURE);
}
39. 数据结构实现
39.1 链表实现
c复制typedef struct Node {
int data;
struct Node *next;
} Node;
void append(Node **head, int data) {
Node *new_node = malloc(sizeof(Node));
new_node->data = data;
new_node->next = NULL;
if (!*head) {
*head = new_node;
return;
}
Node *current = *head;
while (current->next) {
current = current->next;
}
current->next = new_node;
}
39.2 哈希表实现
简单哈希表示例:
c复制#define TABLE_SIZE 100
typedef struct Entry {
char *key;
int value;
struct Entry *next;
} Entry;
Entry *hash_table[TABLE_SIZE];
unsigned int hash(const char *key) {
unsigned long hash = 5381;
int c;
while ((c = *key++)) {
hash = ((hash << 5) + hash) + c;
}
return hash % TABLE_SIZE;
}
40. 编码转换技巧
40.1 UTF-8处理
c复制// 计算UTF-8字符长度
int utf8_len(const char *s) {
int len = 0;
while (*s) {
len++;
if ((*s & 0xC0) != 0x80) s++;
}
return len;
}
40.2 宽字符转换
c复制#include <wchar.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, "");
wchar_t wstr[] = L"中文";
char mbstr[100];
wcstombs(mbstr, wstr, sizeof(mbstr));
printf("%s\n", mbstr);
}
41. 数学计算实践
41.1 高精度计算
处理大整数的加法:
c复制void add_big_numbers(const char *a, const char *b, char *result) {
int len_a = strlen(a);
int len_b = strlen(b);
int max_len = (len_a > len_b) ? len_a : len_b;
int carry = 0;
for (int i = 0; i < max_len; i++) {
int digit_a = (i < len_a) ? a[len_a-1-i] - '0' : 0;
int digit_b = (i < len_b) ? b[len_b-1-i] - '0' : 0;
int sum = digit_a + digit_b + carry;
result[max_len-i] = (sum % 10) + '0';
carry = sum / 10;
}
if (carry) {
result[0] = carry + '0';
} else {
memmove(result, result+1, max_len);
}
}
41.2 浮点数比较
安全比较方法:
c复制#include <math.h>
#include <float.h>
int almost_equal(double a, double b) {
return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * DBL_EPSILON );
}
42. 随机数生成
42.1 安全随机数
c复制#include <openssl/rand.h>
void generate_random_bytes(unsigned char *buf, int num) {
if (RAND_bytes(buf, num) != 1) {
// 错误处理
}
}
42.2 可重现随机序列
c复制#include <stdlib.h>
int main() {
srand(42); // 固定种子
for (int i = 0; i < 10; i++) {
printf("%d\n", rand() % 100);
}
}
43. 正则表达式应用
43.1 POSIX正则
c复制#include <regex.h>
int match_pattern(const char *str, const char *pattern) {
regex_t regex;
if (regcomp(®ex, pattern, REG_EXTENDED) != 0) {
return 0;
}
int ret = regexec(®ex, str, 0, NULL, 0);
regfree(®ex);
return ret == 0;
}
43.2 常见模式示例
验证电子邮件格式:
c复制const char *email_regex =
"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
44. 系统信息获取
44.1 CPU信息读取
Linux下示例:
c复制#include <unistd.h>
int get_cpu_count() {
return sysconf(_SC_NPROCESSORS_ONLN);
}
44.2 内存使用情况
c复制#include <sys/sysinfo.h>
void print_memory_info() {
struct sysinfo info;
sysinfo(&info);
printf("Total RAM: %lu MB\n", info.totalram / 1024 / 1024);
printf("Free RAM: %lu MB\n", info.freeram / 1024 / 1024);
}
45. 时间处理进阶
45.1 高精度计时
c复制#include <time.h>
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// 被测代码
clock_gettime(CLOCK_MONOTONIC, &end);
double elapsed = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1e9;
45.2 时区转换
c复制#include <time.h>
void print_local_time(time_t t) {
struct tm *local = localtime(&t);
char buf[100];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
printf("%s\n", buf);
}
46. 命令行参数解析
46.1 getopt使用
c复制#include <unistd.h>
int main(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "ab:c:")) != -1) {
switch (opt) {
case 'a':
printf("Option a\n");
break;
case 'b':
printf("Option b with value %s\n", optarg);
break;
case '?':
printf("Unknown option\n");
break;
}
}
}
46.2 现代参数解析
考虑使用第三方库:
- argp (GNU)
- argparse
- popt
47. 环境变量操作
47.1 获取环境变量
c复制#include <stdlib.h>
int main() {
char *path = getenv("PATH");
if (path) {
printf("PATH: %s\n", path);
}
}
47.2 设置环境变量
c复制#include <stdlib.h>
int main() {
setenv("MY_VAR", "value", 1);
// ...
unsetenv("MY_VAR");
}
48. 文件系统操作
48.1 递归目录遍历
c复制#include <dirent.h>
void list_dir(const char *path) {
DIR *dir = opendir(path);
if (!dir) return;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) {
continue;
}
printf("%s\n", entry->d_name);
if (entry->d_type == DT_DIR) {
char new_path[PATH_MAX];
snprintf(new_path, sizeof(new_path), "%s/%s", path, entry->d_name);
list_dir(new_path);
}
}
closedir(dir);
}
48.2 文件信息获取
c复制#include <sys/stat.h>
void print_file_info(const char *path) {
struct stat st;
if (stat(path, &st) != 0) {
perror("stat failed");
return;
}
printf("Size: %ld bytes\n", st.st_size
