C语言核心概念复习(三)

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;
}
相关推荐
烧烧的酒0.o2 小时前
Java——JavaSE完整教程
java·开发语言·学习
明洞日记2 小时前
【软考每日一练030】软件维护:逆向工程与再工程的区别与联系
c++·软件工程·软考·逆向工程
池央2 小时前
贪心算法-递增的三页子序列
算法·贪心算法
lrh1228002 小时前
详解K近邻(KNN)算法:原理、实现与优化
算法·机器学习
嗯嗯**2 小时前
Neo4j学习4:数据导入
学习·neo4j·图数据库·csv·数据导入
卡布叻_星星2 小时前
清理缓存并重启 IDEA
笔记
代码游侠2 小时前
学习笔记——Linux内核与嵌入式开发2
linux·运维·arm开发·嵌入式硬件·学习·架构
我是黄骨鱼2 小时前
【零基础学数据库|第四篇】SQL通用语法学习
学习
郝学胜-神的一滴2 小时前
深入Linux网络编程:accept函数——连接请求的“摆渡人”
linux·服务器·开发语言·网络·c++·程序人生