C语言核心知识(第三部分:函数、文件、内存管理、高级特性)
一、函数
1.1 函数基础
复制代码
// 1. 函数声明(原型)
int add(int a, int b); // 告诉编译器函数存在
void print_info(void); // 无参数
// 2. 函数定义
int add(int a, int b) {
return a + b; // 返回值
}
// 3. 函数调用
int result = add(10, 20); // 调用函数
// 4. 无返回值函数
void print_message(char *msg) {
printf("%s\n", msg); // 没有return语句
}
1.2 参数传递方式
复制代码
// 1. 值传递(默认)
void change_value(int x) {
x = 100; // 只修改副本
}
// 2. 地址传递(指针)
void change_real(int *p) {
*p = 100; // 修改原变量
}
// 3. 数组传递
void print_array(int arr[], int n) { // arr实际是指针
for(int i=0; i<n; i++) {
printf("%d ", arr[i]);
}
}
1.3 递归函数
复制代码
// 阶乘:n! = n * (n-1)!
int factorial(int n) {
if(n <= 1) return 1; // 终止条件
return n * factorial(n-1); // 递归调用
}
// 斐波那契数列
int fibonacci(int n) {
if(n <= 1) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
二、文件操作
2.1 文件打开关闭
复制代码
#include <stdio.h>
FILE *fp;
// 打开文件
fp = fopen("file.txt", "r"); // 只读
if(fp == NULL) {
perror("打开失败");
return;
}
// 文件模式
// "r":只读,文件必须存在
// "w":只写,创建或清空
// "a":追加,创建或追加
// "r+":读写,文件必须存在
// "w+":读写,创建或清空
// "a+":读写,创建或追加
// "b":二进制模式(如"rb")
// 关闭文件
fclose(fp);
fp = NULL; // 避免野指针
2.2 文件读写函数
复制代码
// 1. 字符读写
int ch;
ch = fgetc(fp); // 读取一个字符
fputc('A', fp); // 写入一个字符
// 2. 字符串读写
char str[100];
fgets(str, 100, fp); // 读取一行(最多99字符)
fputs("Hello", fp); // 写入字符串
// 3. 格式化读写
int num;
float value;
fscanf(fp, "%d %f", &num, &value); // 格式化读取
fprintf(fp, "Number: %d\n", num); // 格式化写入
// 4. 块读写
struct Data data;
fread(&data, sizeof(struct Data), 1, fp); // 读取一个结构体
fwrite(&data, sizeof(struct Data), 1, fp); // 写入一个结构体
2.3 文件定位
复制代码
// 获取当前位置
long pos = ftell(fp);
// 移动到开头
rewind(fp); // 等价于 fseek(fp, 0, SEEK_SET)
// 移动文件指针
fseek(fp, 10, SEEK_SET); // 从开头移动10字节
fseek(fp, -5, SEEK_CUR); // 从当前位置回退5字节
fseek(fp, 0, SEEK_END); // 移动到文件末尾
// 检测文件结尾
while(!feof(fp)) {
// 处理文件
}
// 错误检测
if(ferror(fp)) {
printf("文件错误\n");
}
clearerr(fp); // 清除错误标志
三、动态内存管理
3.1 内存分配函数
复制代码
#include <stdlib.h>
// 1. malloc - 分配内存(不初始化)
int *p = (int*)malloc(10 * sizeof(int));
if(p == NULL) {
// 分配失败处理
}
// 2. calloc - 分配并清零
int *arr = (int*)calloc(10, sizeof(int)); // 全部初始化为0
// 3. realloc - 重新分配
arr = (int*)realloc(arr, 20 * sizeof(int)); // 扩大或缩小
// 4. free - 释放内存
free(p);
free(arr);
p = NULL; // 避免悬空指针
arr = NULL;
3.2 内存操作函数
复制代码
#include <string.h>
// 1. memset - 内存设置
int buffer[100];
memset(buffer, 0, sizeof(buffer)); // 全部设为0
memset(buffer, 0xFF, sizeof(buffer)); // 全部设为0xFF
// 2. memcpy - 内存复制
int src[5] = {1,2,3,4,5};
int dest[5];
memcpy(dest, src, 5 * sizeof(int));
// 3. memmove - 安全内存移动(处理重叠)
memmove(dest, src, 5 * sizeof(int));
// 4. memcmp - 内存比较
if(memcmp(src, dest, 5 * sizeof(int)) == 0) {
printf("内存块相同\n");
}
3.3 内存管理最佳实践
复制代码
// 1. 总是检查返回值
void* safe_malloc(size_t size) {
void *ptr = malloc(size);
if(!ptr) {
fprintf(stderr, "内存分配失败\n");
exit(EXIT_FAILURE);
}
return ptr;
}
// 2. 避免内存泄漏
void process_data() {
char *buffer = malloc(1024);
if(!buffer) return;
// 使用buffer...
free(buffer); // 必须释放!
buffer = NULL;
}
// 3. 防止重复释放
void safe_free(void **ptr) {
if(ptr && *ptr) {
free(*ptr);
*ptr = NULL; // 设为NULL避免重复释放
}
}
四、预处理器高级特性
4.1 条件编译进阶
复制代码
// 1. 检测编译器
#ifdef __GNUC__
// GCC编译器特有代码
#endif
#ifdef _MSC_VER
// MSVC编译器特有代码
#endif
// 2. 检测平台
#ifdef __linux__
#define PLATFORM "Linux"
#elif defined(_WIN32)
#define PLATFORM "Windows"
#elif defined(__APPLE__)
#define PLATFORM "macOS"
#else
#define PLATFORM "Unknown"
#endif
// 3. 版本控制
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#if VERSION_MAJOR > 1
// 版本1以上的功能
#endif
// 4. 调试模式
#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
4.2 宏的高级用法
复制代码
// 1. 字符串化运算符 #
#define STRINGIFY(x) #x
char *str = STRINGIFY(hello); // 变成 "hello"
// 2. 连接运算符 ##
#define CONCAT(a,b) a##b
int CONCAT(var,1) = 10; // 变成 int var1 = 10;
// 3. 可变参数宏
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
LOG("值: %d, 名称: %s\n", value, name);
// 4. 多语句宏(使用do-while)
#define SWAP(a,b) do { \
typeof(a) temp = a; \
a = b; \
b = temp; \
} while(0)
// 5. 保护宏
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#undef MIN // 取消定义
#define MIN(a,b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a < _b ? _a : _b; \
}) // 重新定义(更安全)
五、位字段与位运算
5.1 位字段(位域)
复制代码
// 定义位字段结构体
struct Flags {
unsigned int is_readonly : 1; // 1位
unsigned int is_hidden : 1; // 1位
unsigned int is_system : 1; // 1位
unsigned int file_type : 3; // 3位(0-7)
unsigned int reserved : 26; // 26位
};
// 使用位字段
struct Flags file_flags;
file_flags.is_readonly = 1;
file_flags.is_hidden = 0;
file_flags.file_type = 5; // 只能存储0-7的值
// 位字段大小
printf("位字段大小: %lu字节\n", sizeof(struct Flags));
5.2 位运算实用技巧
复制代码
// 1. 设置位
#define SET_BIT(var, bit) ((var) |= (1 << (bit)))
#define CLEAR_BIT(var, bit) ((var) &= ~(1 << (bit)))
#define TOGGLE_BIT(var, bit) ((var) ^= (1 << (bit)))
#define CHECK_BIT(var, bit) ((var) & (1 << (bit)))
// 2. 交换值(不使用临时变量)
void swap(int *a, int *b) {
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
// 3. 判断奇偶
int is_odd(int n) {
return n & 1; // 返回1表示奇数,0表示偶数
}
// 4. 乘以2的n次方
int multiply_power_of_2(int x, int n) {
return x << n; // x * 2^n
}
// 5. 除以2的n次方
int divide_power_of_2(int x, int n) {
return x >> n; // x / 2^n
}
// 6. 取模(对2的幂)
int mod_power_of_2(int x, int n) {
return x & ((1 << n) - 1); // x % (2^n)
}
六、错误处理
6.1 errno和错误处理
复制代码
#include <errno.h>
#include <string.h>
// 1. 使用errno
FILE *fp = fopen("nonexistent.txt", "r");
if(fp == NULL) {
printf("错误号: %d\n", errno); // 错误代码
printf("错误信息: %s\n", strerror(errno)); // 错误描述
perror("fopen失败"); // 自动添加错误信息
}
// 2. 常见errno值
// ENOENT: 文件不存在
// EACCES: 权限不足
// ENOMEM: 内存不足
// EINVAL: 无效参数
// 3. 清除errno
errno = 0; // 清除之前的错误
// 4. 错误处理宏
#define CHECK_ERROR(expr) \
do { \
errno = 0; \
if(!(expr)) { \
fprintf(stderr, "错误[%s:%d]: %s\n", \
__FILE__, __LINE__, strerror(errno)); \
exit(EXIT_FAILURE); \
} \
} while(0)
6.2 断言
复制代码
#include <assert.h>
// 1. 基本断言
void process(int *ptr, int size) {
assert(ptr != NULL); // 检查空指针
assert(size > 0); // 检查有效大小
// 处理逻辑
}
// 2. 调试时启用,发布时禁用
// 编译时加 -DNDEBUG 禁用断言
// 3. 自定义断言宏
#ifndef NDEBUG
#define CUSTOM_ASSERT(expr, msg) \
do { \
if(!(expr)) { \
fprintf(stderr, "断言失败[%s:%d]: %s\n", \
__FILE__, __LINE__, msg); \
abort(); \
} \
} while(0)
#else
#define CUSTOM_ASSERT(expr, msg) ((void)0)
#endif
七、编译与链接
7.1 编译过程
复制代码
源文件(.c) → 预处理(.i) → 编译(.s) → 汇编(.o) → 链接(可执行文件)
7.2 存储类别
复制代码
// 1. 自动存储期(auto)- 默认
void func() {
auto int x; // 等同于 int x;
} // 函数结束自动销毁
// 2. 静态存储期(static)
// - 静态局部变量:函数内保持值
// - 静态全局变量:文件内可见
// - 静态函数:文件内可见
// 3. 线程存储期(C11)
_Thread_local int thread_var; // 每个线程有自己的副本
// 4. 动态存储期(malloc/free)
int *p = malloc(sizeof(int)); // 手动管理生命周期
7.3 类型限定符
复制代码
// 1. const - 只读
const int MAX = 100; // 常量
// 2. volatile - 易变
volatile int *hw_reg; // 硬件寄存器
// 3. restrict - 限制别名(C99)
void copy(int *restrict dest,
const int *restrict src, int n);
// 4. _Atomic - 原子类型(C11)
#include <stdatomic.h>
_Atomic int atomic_counter; // 线程安全的计数器
八、C标准库重要函数
8.1 数学函数
复制代码
#include <math.h>
double result;
result = sqrt(16.0); // 平方根: 4.0
result = pow(2.0, 3.0); // 幂运算: 8.0
result = sin(3.14159/2); // 正弦: 1.0
result = log(10.0); // 自然对数
result = fabs(-5.5); // 绝对值: 5.5
result = ceil(3.14); // 向上取整: 4.0
result = floor(3.14); // 向下取整: 3.0
result = round(3.5); // 四舍五入: 4.0
8.2 时间函数
复制代码
#include <time.h>
time_t now;
struct tm *timeinfo;
char buffer[80];
time(&now); // 获取当前时间
timeinfo = localtime(&now); // 转换为本地时间
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
printf("当前时间: %s\n", buffer);
// 计算时间差
time_t start = time(NULL);
// 执行一些操作...
time_t end = time(NULL);
double elapsed = difftime(end, start);
printf("耗时: %.2f秒\n", elapsed);
8.3 随机数
复制代码
#include <stdlib.h>
#include <time.h>
srand(time(NULL)); // 初始化随机种子
int random_num;
random_num = rand(); // 0到RAND_MAX之间的随机数
random_num = rand() % 100; // 0到99之间的随机数
random_num = rand() % 50 + 50; // 50到99之间的随机数
// 生成指定范围的随机数
int random_range(int min, int max) {
return rand() % (max - min + 1) + min;
}