百度云官网谷歌seo详细教学
C语言学习知识点总结(适合新手/考试复习)
🎯 写在前面:这份学习笔记是我在学习C语言过程中的心得体会。作为一名曾经的编程小白,我深知学习编程的不易。因此,我尽量用通俗易懂的语言,结合实际例子,帮助大家更好地理解C语言。
C语言知识点总结
- C语言学习知识点总结(适合新手/考试复习)
- 第一部分:C语言基础
- 1. 初识C语言
- 1.1 第一个C程序
- 1.2 基本数据类型
- 1.3 格式化输入输出
- 2. 运算符与表达式
- 2.1 基本运算符
- 2.2 关系运算符和逻辑运算符
- 2.3 赋值运算符和条件运算符
- 3. 程序流程控制
- 3.1 条件语句
- 3.2 循环语句
- 4. 数组
- 4.1 一维数组基础
- 4.2 二维数组
- 4.3 数组的高级应用
- 5. 函数
- 5.1 函数基础
- 5.2 函数高级特性
- 5.3 函数优化技术
- 6. 指针
- 6.1 指针基础
- 6.2 指针的高级应用
- 6.3 指针的安全性和优化
- 7. 结构体和共用体
- 7.1 结构体基础
- 7.2 结构体的高级特性
- 7.3 共用体和枚举
- 8. 文件操作
- 8.1 文件的基本操作
- 8.2 文本文件操作
- 8.3 二进制文件操作
- 8.4 高级文件操作
- 第二部分:进阶知识点
- 1. 预处理指令
- 1.1 预处理器基础
- 1.2 高级预处理技术
- 1.3 预处理器最佳实践
- 2. 动态内存分配
- 2.1 内存分配基础
- 2.2 高级内存管理
- 2.3 内存管理最佳实践
- 3. 位运算
- 3.1 基本位运算
- 3.2 位运算技巧
- 3.3 位运算高级应用
- 4. 排序算法
- 4.1 基本排序算法
- 4.2 高级排序算法
- 4.3 排序算法的比较和应用
- 5. 查找算法
- 5.1 静态查找
- 5.2 动态查找
- 5.3 哈希查找
- 6. 字符串算法
- 6.1 字符串反转
- 6.2 KMP字符串匹配
- 7. 数学算法
- 7.1 最大公约数(GCD)
- 7.2 素数筛选(埃氏筛)
- 8. 图算法
- 8.1 深度优先搜索(DFS)
- 8.2 广度优先搜索(BFS)
- 9. 动态规划
- 9.1 斐波那契数列
- 9.2 背包问题
第一部分:C语言基础
在开始学习C语言之前,我们先要理解:为什么要学习C语言?C语言是一门"古老"但强大的编程语言,它是很多其他编程语言的基础,学好C语言就像打好地基一样重要。它的特点是:
- 语言简洁,执行效率高
- 贴近硬件,可以直接操作内存
- 应用广泛,从操作系统到单片机都有它的身影
1. 初识C语言
让我们从最基础的开始,就像学习任何一门新语言一样,先从"你好,世界"开始。
1.1 第一个C程序
下面这个程序可能看起来很简单,但它包含了C语言最基本的要素:
#include <stdio.h>int main() {printf("Hello, World!\n");return 0;
}
让我们一行一行地解释这个程序:
#include <stdio.h>
:这是一个预处理指令,告诉编译器我们需要使用输入输出函数。就像要用微信,首先得安装它一样。int main()
:这是程序的入口,所有C程序都从这里开始执行。就像一本书必须有目录一样。printf()
:这是一个输出函数,用来在屏幕上显示文字。return 0
:告诉系统程序正常结束了。
🌟 初学者常见问题:
- 忘记写分号(;)
- main函数名写错(比如写成Main)
- 忘记引入stdio.h就使用printf
- 字符串忘记加双引号
1.2 基本数据类型
在C语言中,数据类型就像是不同的容器,用来存放不同类型的数据。就像我们生活中用不同的容器来存放不同的东西:水要用水杯,饭要用饭盒。
#include <stdio.h>int main() {// 整型:用来存储整数int a = 10; // 最常用的整数类型,比如年龄short b = 20; // 短整数,节省内存空间long c = 30L; // 长整数,可以存储更大的数// 浮点型:用来存储小数float d = 3.14f; // 单精度浮点数,比如身高double e = 3.14159; // 双精度浮点数,比如圆周率// 字符型:用来存储单个字符char f = 'A'; // 字符,比如等级(A、B、C)// 输出各种类型的值printf("整数:%d\n", a);printf("浮点数:%f\n", d);printf("字符:%c\n", f);return 0;
}
🎯 数据类型的选择建议:
- 存储普通整数,用
int
就够了 - 需要小数点,用
float
或double
- 存储单个字符,用
char
- 需要很大的数,才考虑用
long
💡 生活中的例子:
- 年龄:用
int
(不会有小数的年龄) - 体重:用
float
(需要小数点) - 成绩等级:用
char
(A、B、C、D) - 银行存款:用
double
(需要精确到分)
1.3 格式化输入输出
在C语言中,格式化输入输出就像是我们日常生活中的对话。printf是"说话",scanf是"听别人说"。让我们来看看如何让程序和用户进行友好的对话:
#include <stdio.h>int main() {// 格式化输出int num = 123;float pi = 3.14159;char str[] = "Hello";printf("整数格式:\n");printf("默认右对齐:%8d\n", num); // " 123"printf("左对齐:%-8d\n", num); // "123 "printf("零填充:%08d\n", num); // "00000123"printf("\n浮点数格式:\n");printf("默认小数点后6位:%f\n", pi); // "3.141590"printf("指定精度:%.2f\n", pi); // "3.14"printf("科学计数法:%e\n", pi); // "3.141590e+00"printf("\n字符串格式:\n");printf("默认:%s\n", str); // "Hello"printf("限制宽度:%8s\n", str); // " Hello"printf("限制长度:%.3s\n", str); // "Hel"
}
🎨 格式控制符的妙用:
-
整数格式化:
%d
:最基本的整数显示,就像写数字一样%8d
:预留8个空格,就像在表格中对齐数字%08d
:用0填充空位,就像银行账号的显示方式
-
小数格式化:
%f
:显示小数,默认保留6位小数%.2f
:保留2位小数,适合显示价格%e
:科学计数法,适合表示很大或很小的数
-
字符串格式化:
%s
:显示字符串,就像写一段文字%8s
:控制显示宽度,适合制作表格%.3s
:限制显示长度,类似微博字数限制
💡 实际应用场景:
- 显示商品价格:
%.2f
- 对齐账单数据:
%8d
- 显示百分比:
%.1f%%
- 格式化日期时间:
%02d:%02d:%02d
🚫 常见错误提醒:
-
格式符和实际类型不匹配:
int num = 10; printf("%f", num); // 错误!整数用%d
-
忘记取地址符&:
int age; scanf("%d", age); // 错误!应该是&age
✨ 小技巧:
-
制作表格时,可以这样对齐:
printf("姓名 年龄 成绩\n"); printf("%-8s%4d%8.1f\n", "张三", 18, 92.5); printf("%-8s%4d%8.1f\n", "李四", 19, 88.0);
-
读取带空格的字符串:
char name[50]; printf("请输入姓名:"); gets(name); // 可以读取带空格的字符串
2. 运算符与表达式
在C语言中,运算符就像是数学中的运算符号,但功能更加强大。让我们通过生活中的例子来理解它们。
2.1 基本运算符
就像我们在小学学习的加减乘除一样,C语言中的基本运算符也很容易理解:
#include <stdio.h>int main() {int a = 10, b = 3;// 算术运算符printf("基本运算:\n");printf("%d + %d = %d\n", a, b, a + b); // 就像买东西算总价printf("%d - %d = %d\n", a, b, a - b); // 就像找零钱printf("%d × %d = %d\n", a, b, a * b); // 就像买3件10元的商品printf("%d ÷ %d = %d\n", a, b, a / b); // 整数除法,结果取整printf("%d 除以 %d 的余数是:%d\n", a, b, a % b); // 就像分苹果剩下的// 自增自减(就像计数器)int count = 5;printf("\n计数器示例:\n");printf("当前人数:%d\n", count);printf("一个人进来:%d\n", ++count); // 先加1再使用printf("一个人出去:%d\n", --count); // 先减1再使用return 0;
}
💡 生活中的例子:
- 加法(+):购物时计算总价
- 减法(-):找零钱、计算年龄差
- 乘法(*):计算商品总价(单价×数量)
- 除法(/):平均分配(10个苹果分给3个人)
- 取余(%):判断奇偶、计算剩余部分
⚠️ 需要注意的点:
- 整数除法会直接舍弃小数部分
- 除数不能为0
- 取余运算只能用于整数
2.2 关系运算符和逻辑运算符
这些运算符就像是我们生活中的判断,帮助我们做出决策:
#include <stdio.h>int main() {int age = 18;int score = 85;// 关系运算符(就像比较大小)printf("年龄判断:\n");printf("是否成年:%d\n", age >= 18); // 1表示是,0表示否printf("是否到退休年龄:%d\n", age >= 60);// 逻辑运算符(就像组合条件)printf("\n考试成绩评估:\n");// 同时满足多个条件(&&)printf("是否及格且表现优秀:%d\n", score >= 60 && score < 90);// 满足任意一个条件(||)printf("是否需要补考或者重修:%d\n", score < 60 || score > 100);return 0;
}
🌟 实际应用场景:
-
关系运算符:
- 判断年龄是否达到标准
- 比较成绩高低
- 检查库存是否充足
-
逻辑运算符:
- 判断是否满足多个条件(&&)
- 判断是否满足任意条件(||)
- 判断条件是否不成立(!)
2.3 赋值运算符和条件运算符
这些是C语言中的"效率工具",可以让我们的代码更简洁:
#include <stdio.h>int main() {// 赋值运算符(简化写法)int money = 100;printf("初始金额:%d\n", money);money += 50; // 等同于 money = money + 50printf("存入50元后:%d\n", money);money -= 30; // 等同于 money = money - 30printf("取出30元后:%d\n", money);// 条件运算符(三目运算符)- 简化if-elseint score = 75;printf("\n成绩评估:%s\n", score >= 60 ? "及格" : "不及格");// 实际应用组合int age = 20;int ticket = 100;int final_price = ticket * (age < 12 ? 0.5 : 1.0);printf("票价:%d元\n", final_price);return 0;
}
✨ 使用技巧:
-
复合赋值运算符:
+=
:累加(比如积分增加)-=
:递减(比如库存减少)*=
:倍数增长(比如利息计算)
-
条件运算符:
- 适合简单的条件选择
- 可以用在表达式中
- 代码更简单
3. 程序流程控制
让我们把程序流程控制想象成生活中的决策和重复动作。每个人每天都在不知不觉中做出很多决定,执行很多重复的动作,这些就是程序中的流程控制。
3.1 条件语句
条件语句就像我们每天都要面对的选择,比如:
- 如果下雨了,就带伞
- 如果饿了,就吃饭
- 如果累了,就休息
#include <stdio.h>int main() {// if-else语句示例:模拟考试成绩评级系统int score = 85;printf("=== 成绩评估系统 ===\n");if (score >= 90) {printf("🌟 优秀!继续保持!\n");} else if (score >= 80) {printf("👍 良好!还可以更好!\n");} else if (score >= 60) {printf("😊 及格!需要继续努力!\n");} else {printf("💪 加油!不要灰心!\n");}// switch语句示例:模拟简单点餐系统int choice = 1;printf("\n=== 快餐点餐系统 ===\n");switch (choice) {case 1:printf("🍔 您选择了汉堡套餐\n");break;case 2:printf("🍗 您选择了炸鸡套餐\n");break;case 3:printf("🍝 您选择了意面套餐\n");break;default:printf("❓ 对不起,没有这个选项\n");}return 0;
}
💡 实际应用场景:
-
if-else适用场景:
- 根据年龄判断票价优惠
- 根据消费金额计算折扣
- 根据天气选择出行方式
-
switch适用场景:
- 菜单选择系统
- 简单的状态机
- 等级评定系统
⚠️ 易错点提醒:
-
if-else中的常见错误:
- 忘记写大括号{}
- 判断条件写成=(赋值)而不是==(比较)
- 条件的范围重叠或遗漏
-
switch中的常见错误:
- 忘记写break导致执行多个case
- case中的值必须是常量
- default位置可以放在任意位置(但建议放在最后)
3.2 循环语句
循环就像我们生活中的:
- 每天刷牙
- 每周健身
- 每月查看账单
#include <stdio.h>int main() {// for循环示例:模拟健身计划printf("=== 健身计划追踪 ===\n");for (int day = 1; day <= 7; day++) {printf("第%d天:完成%d个俯卧撑 💪\n", day, day * 5); // 每天增加5个}// while循环示例:模拟存钱计划printf("\n=== 存钱计划追踪 ===\n");int savings = 0;int week = 1;while (savings < 1000) {savings += 200; // 每周存200printf("第%d周:已存%d元 💰\n", week++, savings);}// do-while循环示例:模拟游戏菜单int choice;do {printf("\n=== 游戏菜单 ===\n");printf("1. 开始游戏 🎮\n");printf("2. 设置 ⚙️\n");printf("3. 退出 🚪\n");printf("请选择(1-3):");scanf("%d", &choice);} while (choice < 1 || choice > 3);return 0;
}
🎯 实践建议:
-
选择合适的循环:
- for:知道确切循环次数
- while:不确定循环次数,但知道结束条件
- do-while:至少需要执行一次的情况
-
循环优化技巧:
- 使用break提前结束循环
- 使用continue跳过当前循环
- 避免死循环(一定要有正确的终止条件)
4. 数组
4.1 一维数组基础
#include <stdio.h>int main() {// 数组的多种定义和初始化方式int arr1[5] = {1, 2, 3, 4, 5}; // 完整初始化int arr2[5] = {1, 2}; // 部分初始化,其余为0int arr3[] = {1, 2, 3}; // 自动确定大小int arr4[5] = {0}; // 全部初始化为0// 数组基本操作printf("=== 数组基本操作 ===\n");// 1. 访问元素printf("第一个元素:%d\n", arr1[0]);printf("最后一个元素:%d\n", arr1[4]);// 2. 修改元素arr1[0] = 10;// 3. 计算数组大小int size = sizeof(arr1) / sizeof(arr1[0]);printf("数组大小:%d\n", size);// 4. 数组遍历的多种方式// 4.1 下标遍历for (int i = 0; i < size; i++) {printf("%d ", arr1[i]);}printf("\n");// 4.2 指针遍历int *p = arr1;for (int i = 0; i < size; i++) {printf("%d ", *(p + i));}printf("\n");return 0;
}
⚠️ 关键技术点:
-
数组定义规则:
- 数组大小必须是常量表达式
- 数组名代表数组首地址
- 下标从0开始
-
内存特性:
- 连续内存空间
- 固定大小,不可变
- 随机访问,O(1)时间复杂度
-
数组越界问题:
- 访问越界可能导致程序崩溃
- 编译器可能不会检查越界
- 需要手动确保下标合法
4.2 二维数组
#include <stdio.h>int main() {// 1. 二维数组的多种定义方式int arr1[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};int arr2[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; // 按行展开初始化// 2. 二维数组的访问printf("=== 二维数组访问 ===\n");// 2.1 使用行列下标printf("arr1[1][2] = %d\n", arr1[1][2]);// 2.2 使用指针printf("*(*arr1 + 1) = %d\n", *(*arr1 + 1));// 3. 二维数组的遍历int rows = sizeof(arr1) / sizeof(arr1[0]);int cols = sizeof(arr1[0]) / sizeof(arr1[0][0]);// 3.1 按行优先遍历for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%3d ", arr1[i][j]);}printf("\n");}// 3.2 按列优先遍历for (int j = 0; j < cols; j++) {for (int i = 0; i < rows; i++) {printf("%3d ", arr1[i][j]);}printf("\n");}return 0;
}
🔍 二维数组核心要点:
-
内存布局:
- 行优先存储
- 连续内存空间
- 可以通过一维数组方式访问
-
地址计算:
- 元素地址 = 基地址 + (i * 列数 + j) * sizeof(元素类型)
- arr[i][j] 等价于 ((arr + i) + j)
-
参数传递:
- 作为函数参数时必须指定列数
- 可以省略行数,但不能省略列数
4.3 数组的高级应用
#include <stdio.h>// 1. 数组作为函数参数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}// 2. 返回数组的函数(通过指针)
int* findElement(int arr[], int size, int target) {for (int i = 0; i < size; i++) {if (arr[i] == target) {return &arr[i];}}return NULL;
}// 3. 二维数组作为函数参数
void process2DArray(int arr[][4], int rows) {for (int i = 0; i < rows; i++) {for (int j = 0; j < 4; j++) {arr[i][j] *= 2;}}
}int main() {int arr[] = {1, 2, 3, 4, 5};int size = sizeof(arr) / sizeof(arr[0]);// 数组作为参数传递printf("原始数组:");printArray(arr, size);// 查找元素int target = 3;int *found = findElement(arr, size, target);if (found != NULL) {printf("找到元素 %d 在位置:%ld\n", target, found - arr);}// 二维数组操作int matrix[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};process2DArray(matrix, 3);return 0;
}
📝 高级特性总结:
-
数组与指针:
- 数组名可以作为指针使用
- 数组作为参数会退化为指针
- 指针算术运算与数组索引等价
-
内存管理:
- 栈上分配的数组大小固定
- 需要可变大小时使用动态内存分配
- 注意避免内存泄漏
-
性能优化:
- 合理利用CPU缓存
- 避免频繁的数组拷贝
- 使用合适的遍历方式
5. 函数
5.1 函数基础
#include <stdio.h>// 1. 函数声明
int add(int a, int b);
void swap(int *x, int *y);
int getMax(int arr[], int size);// 2. 函数定义
int add(int a, int b) {return a + b;
}void swap(int *x, int *y) {int temp = *x;*x = *y;*y = temp;
}int getMax(int arr[], int size) {int max = arr[0];for(int i = 1; i < size; i++) {if(arr[i] > max) max = arr[i];}return max;
}int main() {// 3. 函数调用示例printf("=== 函数调用演示 ===\n");// 3.1 基本函数调用int sum = add(5, 3);printf("add(5, 3) = %d\n", sum);// 3.2 传递指针参数int x = 10, y = 20;printf("交换前: x = %d, y = %d\n", x, y);swap(&x, &y);printf("交换后: x = %d, y = %d\n", x, y);// 3.3 传递数组参数int arr[] = {64, 34, 25, 12, 22, 11, 90};int size = sizeof(arr) / sizeof(arr[0]);printf("数组最大值: %d\n", getMax(arr, size));return 0;
}
📝 函数基础要点:
-
函数声明:
- 函数原型声明
- 参数列表
- 返回值类型
-
参数传递:
- 值传递:参数为基本数据类型
- 指针传递:需要修改原值
- 数组传递:退化为指针
-
返回值:
- void:无返回值
- 基本数据类型
- 指针类型
5.2 函数高级特性
#include <stdio.h>// 1. 内联函数
inline int square(int x) {return x * x;
}// 2. 函数指针
int operate(int x, int y, int (*operation)(int, int)) {return operation(x, y);
}int multiply(int x, int y) { return x * y; }
int divide(int x, int y) { return x / y; }// 3. 可变参数函数
#include <stdarg.h>int sum(int count, ...) {va_list args;va_start(args, count);int total = 0;for(int i = 0; i < count; i++) {total += va_arg(args, int);}va_end(args);return total;
}// 4. 递归函数
unsigned long long factorial(int n) {if(n <= 1) return 1;return n * factorial(n - 1);
}int main() {printf("=== 函数高级特性演示 ===\n");// 1. 内联函数调用printf("square(5) = %d\n", square(5));// 2. 函数指针使用int (*operation)(int, int);operation = multiply;printf("multiply(6, 3) = %d\n", operation(6, 3));operation = divide;printf("divide(6, 3) = %d\n", operation(6, 3));// 3. 可变参数函数调用printf("sum(4, 1,2,3,4) = %d\n", sum(4, 1,2,3,4));// 4. 递归函数调用printf("factorial(5) = %llu\n", factorial(5));return 0;
}
🔍 高级特性要点:
-
内联函数:
- 减少函数调用开销
- 编译器优化
- 适用于简短、频繁调用的函数
-
函数指针:
- 指向函数的指针
- 回调函数实现
- 函数表实现
-
可变参数:
- va_list 类型
- va_start/va_arg/va_end 宏
- 参数个数的传递
-
递归函数:
- 基本情况处理
- 递归调用
- 栈空间使用
5.3 函数优化技术
#include <stdio.h>// 1. 参数优化
void processArray(const int *arr, int size) { // 使用const防止修改// 只读操作
}// 2. 返回值优化
typedef struct {int x;int y;
} Point;Point* createPoint(int x, int y) { // 返回堆内存中的对象Point* p = (Point*)malloc(sizeof(Point));if(p != NULL) {p->x = x;p->y = y;}return p;
}// 3. 尾递归优化
int factorial_tail(int n, int acc) {if(n <= 1) return acc;return factorial_tail(n - 1, n * acc);
}int main() {// 函数优化示例int arr[] = {1, 2, 3, 4, 5};processArray(arr, 5);Point* p = createPoint(10, 20);if(p != NULL) {printf("Point: (%d, %d)\n", p->x, p->y);free(p);}printf("Factorial(5) = %d\n", factorial_tail(5, 1));return 0;
}
⚡ 优化技术要点:
-
参数传递优化:
- 使用const修饰只读参数
- 大对象使用指针传递
- 小对象使用值传递
-
返回值优化:
- 返回指针而非大对象
- 使用输出参数
- 考虑内存管理
-
递归优化:
- 尾递归转换
- 递归深度控制
- 内存使用优化
6. 指针
6.1 指针基础
#include <stdio.h>int main() {// 1. 指针的声明和初始化int num = 42;int *ptr = # // 基本指针const int *ptr1 = # // 指向常量的指针int * const ptr2 = # // 常量指针// 2. 指针的基本操作printf("=== 指针基本操作 ===\n");printf("变量值: %d\n", num);printf("变量地址: %p\n", (void*)&num);printf("指针存储的地址: %p\n", (void*)ptr);printf("指针指向的值: %d\n", *ptr);// 3. 指针的算术运算int array[5] = {1, 2, 3, 4, 5};int *p = array;printf("\n=== 指针算术运算 ===\n");printf("初始值: %d\n", *p);p++; // 指针递增printf("递增后: %d\n", *p);printf("偏移访问: %d\n", *(p + 2));return 0;
}
📝 指针基础要点:
-
指针类型:
- 基本指针:可读写指向的值
- 指向常量的指针:不能通过指针修改值
- 常量指针:指针本身不能修改
- void指针:通用指针类型
-
内存操作:
- &:取地址运算符
- *:解引用运算符
- 指针大小:取决于系统架构
-
指针运算:
- 加减运算:按照指针类型的大小进行
- 比较运算:比较地址大小
- NULL指针:表示不指向任何地址
6.2 指针的高级应用
#include <stdio.h>
#include <stdlib.h>// 1. 函数指针
typedef int (*Operation)(int, int);
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }// 2. 回调函数
void processArray(int *arr, int size, int (*process)(int)) {for(int i = 0; i < size; i++) {arr[i] = process(arr[i]);}
}int square(int x) { return x * x; }
int double_value(int x) { return x * 2; }// 3. 多级指针应用
void allocateMatrix(int ***matrix, int rows, int cols) {*matrix = (int**)malloc(rows * sizeof(int*));for(int i = 0; i < rows; i++) {(*matrix)[i] = (int*)malloc(cols * sizeof(int));}
}int main() {// 1. 函数指针使用Operation ops[] = {add, subtract};printf("10 + 5 = %d\n", ops[0](10, 5));printf("10 - 5 = %d\n", ops[1](10, 5));// 2. 回调函数示例int arr[] = {1, 2, 3, 4, 5};printf("\n原始数组: ");for(int i = 0; i < 5; i++) printf("%d ", arr[i]);processArray(arr, 5, square);printf("\n平方后: ");for(int i = 0; i < 5; i++) printf("%d ", arr[i]);// 3. 多级指针示例int **matrix;allocateMatrix(&matrix, 3, 4);// 使用矩阵for(int i = 0; i < 3; i++) {for(int j = 0; j < 4; j++) {matrix[i][j] = i * 4 + j;printf("%2d ", matrix[i][j]);}printf("\n");}// 释放内存for(int i = 0; i < 3; i++) {free(matrix[i]);}free(matrix);return 0;
}
🔍 高级应用要点:
-
函数指针:
- 存储函数地址
- 实现回调机制
- 函数表实现
-
多级指针:
- 动态分配多维数组
- 指针的间接引用
- 参数传递优化
-
内存管理:
- 动态内存分配
- 内存泄漏预防
- 指针安全性
6.3 指针的安全性和优化
#include <stdio.h>
#include <stdlib.h>// 1. 安全的内存分配
void* safeAlloc(size_t size) {void* ptr = malloc(size);if(ptr == NULL) {fprintf(stderr, "内存分配失败\n");exit(1);}return ptr;
}// 2. 智能指针模拟
typedef struct {int* ptr;size_t* ref_count;
} SmartPtr;SmartPtr createSmartPtr(int value) {SmartPtr sp;sp.ptr = (int*)safeAlloc(sizeof(int));sp.ref_count = (size_t*)safeAlloc(sizeof(size_t));*sp.ptr = value;*sp.ref_count = 1;return sp;
}void addRef(SmartPtr* sp) {(*sp->ref_count)++;
}void release(SmartPtr* sp) {(*sp->ref_count)--;if(*sp->ref_count == 0) {free(sp->ptr);free(sp->ref_count);sp->ptr = NULL;sp->ref_count = NULL;}
}int main() {// 1. 安全的指针使用int* ptr = (int*)safeAlloc(sizeof(int));*ptr = 42;printf("安全分配的值: %d\n", *ptr);free(ptr);ptr = NULL; // 避免悬挂指针// 2. 智能指针示例SmartPtr sp1 = createSmartPtr(100);printf("智能指针值: %d\n", *sp1.ptr);printf("引用计数: %zu\n", *sp1.ref_count);// 复制智能指针SmartPtr sp2 = sp1;addRef(&sp2);printf("复制后引用计数: %zu\n", *sp1.ref_count);// 释放智能指针release(&sp1);release(&sp2);return 0;
}
⚡ 安全性和优化要点:
-
内存安全:
- 空指针检查
- 边界检查
- 类型安全
-
资源管理:
- RAII原则
- 引用计数
- 自动内存管理
-
性能优化:
- 指针对齐
- 缓存友好访问
- 避免指针追踪
7. 结构体和共用体
7.1 结构体基础
#include <stdio.h>
#include <string.h>// 1. 结构体定义
struct Point {int x;int y;
};// 2. 结构体对齐和压缩
#pragma pack(1) // 设置1字节对齐
struct CompactData {char a; // 1字节int b; // 4字节short c; // 2字节
};
#pragma pack() // 恢复默认对齐// 3. 结构体嵌套
struct Rectangle {struct Point topLeft;struct Point bottomRight;char *label;
};int main() {// 1. 结构体初始化struct Point p1 = {10, 20}; // 常规初始化struct Point p2 = {.y = 30, .x = 40}; // 指定成员初始化// 2. 内存对齐演示printf("=== 内存对齐 ===\n");printf("Point大小: %zu字节\n", sizeof(struct Point));printf("CompactData大小: %zu字节\n", sizeof(struct CompactData));// 3. 结构体操作struct Rectangle rect;rect.topLeft = p1;rect.bottomRight = p2;rect.label = "Rectangle1";printf("\n=== 结构体访问 ===\n");printf("矩形: (%d,%d) to (%d,%d)\n", rect.topLeft.x, rect.topLeft.y,rect.bottomRight.x, rect.bottomRight.y);return 0;
}
📝 结构体基础要点:
-
内存布局:
- 成员按声明顺序存储
- 考虑内存对齐
- 可能存在填充字节
-
对齐规则:
- 默认对齐:按系统字长
- 自定义对齐:使用pragma pack
- 对齐优化:成员排序
-
初始化方式:
- 顺序初始化
- 指定成员初始化
- 动态分配初始化
7.2 结构体的高级特性
#include <stdio.h>
#include <stdlib.h>// 1. 结构体封装
typedef struct {char *data;size_t length;size_t capacity;
} String;// 构造函数
String* String_create(const char* str) {String* s = (String*)malloc(sizeof(String));if (s != NULL) {s->capacity = strlen(str) + 1;s->data = (char*)malloc(s->capacity);if (s->data != NULL) {strcpy(s->data, str);s->length = strlen(str);} else {free(s);return NULL;}}return s;
}// 析构函数
void String_destroy(String* s) {if (s != NULL) {free(s->data);free(s);}
}// 2. 柔性数组
typedef struct {int length;int data[]; // 柔性数组成员
} Array;Array* Array_create(int length) {Array* arr = (Array*)malloc(sizeof(Array) + length * sizeof(int));if (arr != NULL) {arr->length = length;}return arr;
}// 3. 位域结构
struct Flags {unsigned int read: 1; // 1位unsigned int write: 1; // 1位unsigned int exec: 1; // 1位unsigned int reserved: 5; // 5位
};int main() {// 1. 封装示例String* str = String_create("Hello");if (str != NULL) {printf("字符串: %s, 长度: %zu\n", str->data, str->length);String_destroy(str);}// 2. 柔性数组示例Array* arr = Array_create(5);if (arr != NULL) {for (int i = 0; i < arr->length; i++) {arr->data[i] = i * i;printf("%d ", arr->data[i]);}printf("\n");free(arr);}// 3. 位域示例struct Flags flags = {1, 1, 0, 0}; // 读写权限printf("Flags大小: %zu字节\n", sizeof(struct Flags));printf("权限: r%c w%c x%c\n",flags.read ? 'w' : '-',flags.write ? 'w' : '-',flags.exec ? 'x' : '-');return 0;
}
🔍 高级特性要点:
-
封装技术:
- 构造和析构函数
- 资源管理
- 接口设计
-
柔性数组:
- 必须是最后一个成员
- 不占用结构体大小
- 动态内存分配
-
位域使用:
- 节省内存空间
- 位级操作
- 标志位管理
7.3 共用体和枚举
#include <stdio.h>// 1. 共用体定义
union Data {int i; // 4字节float f; // 4字节char str[8]; // 8字节
};// 2. 枚举定义
enum DayOfWeek {SUNDAY = 0,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY
};// 3. 类型标记联合
typedef enum {TYPE_INT,TYPE_FLOAT,TYPE_STRING
} ValueType;typedef struct {ValueType type;union {int i;float f;char *s;} value;
} TypedValue;// 值打印函数
void printValue(TypedValue *v) {switch (v->type) {case TYPE_INT:printf("整数: %d\n", v->value.i);break;case TYPE_FLOAT:printf("浮点数: %f\n", v->value.f);break;case TYPE_STRING:printf("字符串: %s\n", v->value.s);break;}
}int main() {// 1. 共用体示例union Data data;printf("Data大小: %zu字节\n", sizeof(union Data));data.i = 42;printf("整数: %d\n", data.i);data.f = 3.14f;printf("浮点数: %f\n", data.f);strcpy(data.str, "Hello");printf("字符串: %s\n", data.str);// 2. 枚举示例enum DayOfWeek today = WEDNESDAY;printf("今天是周%d\n", today + 1);// 3. 类型标记联合示例TypedValue val1 = {TYPE_INT, {.i = 42}};TypedValue val2 = {TYPE_FLOAT, {.f = 3.14f}};TypedValue val3 = {TYPE_STRING, {.s = "Hello"}};printValue(&val1);printValue(&val2);printValue(&val3);return 0;
}
⚡ 共用体和枚举要点:
-
共用体特性:
- 成员共享内存
- 大小为最大成员
- 内存覆盖规则
-
枚举特性:
- 常量定义
- 自动递增
- 类型安全
-
应用场景:
- 数据类型转换
- 内存节省
- 状态管理
8. 文件操作
8.1 文件的基本操作
#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {// 1. 文件打开模式FILE *fp;const char *modes[] = {"r", "w", "a", "r+", "w+", "a+"};// 2. 错误处理示例if ((fp = fopen("test.txt", "r")) == NULL) {fprintf(stderr, "错误码: %d\n", errno);fprintf(stderr, "错误信息: %s\n", strerror(errno));return 1;}// 3. 文件指针操作fseek(fp, 0, SEEK_SET); // 移到文件开头fseek(fp, 0, SEEK_END); // 移到文件末尾long size = ftell(fp); // 获取文件大小rewind(fp); // 回到文件开头// 4. 文件关闭fclose(fp);return 0;
}
8.2 文本文件操作
#include <stdio.h>int main() {FILE *fp;char buffer[1024];// 1. 写入文本文件fp = fopen("text.txt", "w");if (fp != NULL) {// 单字符写入fputc('H', fp);// 字符串写入fputs("ello, World!\n", fp);// 格式化写入fprintf(fp, "Count: %d\n", 42);fclose(fp);}// 2. 读取文本文件fp = fopen("text.txt", "r");if (fp != NULL) {// 单字符读取int ch = fgetc(fp);// 行读取if (fgets(buffer, sizeof(buffer), fp) != NULL) {printf("读取的行: %s", buffer);}// 格式化读取int count;if (fscanf(fp, "Count: %d", &count) == 1) {printf("读取的数字: %d\n", count);}fclose(fp);}return 0;
}
8.3 二进制文件操作
#include <stdio.h>
#include <stdlib.h>// 定义数据结构
typedef struct {int id;char name[50];double score;
} Student;int main() {FILE *fp;Student students[3] = {{1, "张三", 85.5},{2, "李四", 92.0},{3, "王五", 78.5}};// 1. 写入二进制文件fp = fopen("students.dat", "wb");if (fp != NULL) {// 整块写入fwrite(students, sizeof(Student), 3, fp);// 单个写入Student newStudent = {4, "赵六", 88.5};fwrite(&newStudent, sizeof(Student), 1, fp);fclose(fp);}// 2. 读取二进制文件fp = fopen("students.dat", "rb");if (fp != NULL) {Student readStudent;// 随机访问fseek(fp, sizeof(Student) * 2, SEEK_SET); // 定位到第三个记录fread(&readStudent, sizeof(Student), 1, fp);printf("ID: %d, 姓名: %s, 分数: %.1f\n",readStudent.id, readStudent.name, readStudent.score);fclose(fp);}return 0;
}
8.4 高级文件操作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 1. 文件缓冲区管理
void bufferDemo() {FILE *fp = fopen("test.txt", "w");if (fp != NULL) {// 设置缓冲区char buffer[1024];setvbuf(fp, buffer, _IOFBF, sizeof(buffer));// 写入数据fprintf(fp, "测试数据\n");// 强制刷新缓冲区fflush(fp);fclose(fp);}
}// 2. 临时文件操作
void tempFileDemo() {FILE *temp = tmpfile();if (temp != NULL) {fprintf(temp, "临时数据\n");rewind(temp);char buffer[100];if (fgets(buffer, sizeof(buffer), temp) != NULL) {printf("临时文件内容: %s", buffer);}fclose(temp); // 关闭时自动删除}
}// 3. 文件锁定示例
void fileLockDemo() {FILE *fp = fopen("shared.txt", "r+");if (fp != NULL) {#ifdef _WIN32_flock(fileno(fp), _LK_NBLCK); // Windows#elseflock(fileno(fp), LOCK_EX); // UNIX#endif// 对文件进行操作fprintf(fp, "加锁写入\n");#ifdef _WIN32_funlock(fileno(fp));#elseflock(fileno(fp), LOCK_UN);#endiffclose(fp);}
}int main() {bufferDemo();tempFileDemo();fileLockDemo();return 0;
}
【核心要点】
-
文件操作基础:
- 文件打开模式的选择
- 错误处理机制
- 文件指针操作
- 资源管理
-
文本文件操作:
- 字符级操作
- 行级操作
- 格式化IO
- 缓冲区管理
-
二进制文件操作:
- 结构体的读写
- 随机访问
- 数据对齐
- 字节序考虑
-
高级特性:
- 缓冲区管理
- 临时文件
- 文件锁定
- 异常处理
⚠️ 注意事项:
- 始终检查文件操作的返回值
- 正确关闭文件以释放资源
- 考虑文件访问权限
- 处理平台差异性
- 注意数据的字节对齐
第二部分:进阶知识点
1. 预处理指令
1.1 预处理器基础
#include <stdio.h>// 1. 宏定义
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))// 2. 条件编译
#define DEBUG 1#if DEBUG#define LOG(msg) printf("[DEBUG] %s\n", msg)
#else#define LOG(msg) // 空定义
#endif// 3. 预定义宏
void printInfo() {printf("文件:%s\n", __FILE__);printf("行号:%d\n", __LINE__);printf("编译时间:%s %s\n", __DATE__, __TIME__);printf("ANSI C:%d\n", __STDC__);
}int main() {// 宏的使用printf("PI = %.5f\n", PI);printf("最大值:%d\n", MAX(10, 20));printf("平方值:%d\n", SQUARE(5));// 条件编译的使用LOG("测试消息");// 预定义宏的使用printInfo();return 0;
}
1.2 高级预处理技术
#include <stdio.h>// 1. 字符串化运算符 #
#define PRINT_VAR(var) printf(#var " = %d\n", var)// 2. 标记连接运算符 ##
#define CONCAT(x, y) x##y// 3. 可变参数宏
#define DEBUG_LOG(format, ...) \printf("[DEBUG] " format "\n", ##__VA_ARGS__)// 4. 条件编译的高级用法
#ifdef _WIN32#define OS "Windows"
#elif defined(__linux__)#define OS "Linux"
#elif defined(__APPLE__)#define OS "macOS"
#else#define OS "Unknown"
#endif// 5. 防止头文件重复包含
#ifndef MY_HEADER_H
#define MY_HEADER_Htypedef struct {int x;int y;
} Point;#endifint main() {// 字符串化示例int count = 42;PRINT_VAR(count); // 输出:count = 42// 标记连接示例int value12 = 100;printf("%d\n", CONCAT(value, 12)); // 访问value12// 可变参数宏示例DEBUG_LOG("测试 %s", "消息");DEBUG_LOG("简单消息");// 条件编译结果printf("当前操作系统:%s\n", OS);return 0;
}
1.3 预处理器最佳实践
// 1. 头文件保护
#ifndef MATH_UTILS_H
#define MATH_UTILS_H// 2. 版本控制
#define MATH_UTILS_VERSION 100 // 1.0.0// 3. 功能开关
#define ENABLE_ADVANCED_MATH 1// 4. 条件检查
#if defined(_MSC_VER) && _MSC_VER < 1400#error "需要 Visual C++ 2005 或更高版本"
#endif// 5. 平台特定代码
#ifdef _WIN32#define PATH_SEPARATOR '\\'
#else#define PATH_SEPARATOR '/'
#endif// 6. 安全的宏定义
#define SAFE_DELETE(p) do { if(p) { delete (p); (p) = NULL; } } while(0)
#define SAFE_ARRAY_DELETE(p) do { if(p) { delete[] (p); (p) = NULL; } } while(0)// 7. 条件编译的嵌套
#if defined(DEBUG)#if defined(_WIN32)#define DEBUG_BREAK() __debugbreak()#else#define DEBUG_BREAK() __builtin_trap()#endif
#else#define DEBUG_BREAK()
#endif#endif // MATH_UTILS_H
【核心要点】
-
预处理器基础:
- 宏定义与函数的区别
- 条件编译的使用场景
- 预定义宏的应用
-
高级预处理技术:
- 字符串化运算符(#)
- 标记连接运算符(##)
- 可变参数宏
- 条件编译的高级应用
-
最佳实践:
- 头文件保护机制
- 版本控制和功能开关
- 平台特定代码处理
- 安全的宏定义方式
⚠️ 注意事项:
- 宏定义中的括号使用
- 避免宏的副作用
- 合理使用条件编译
- 注意宏的命名规范
- 预处理器的执行顺序
🔍 常见应用场景:
- 调试信息控制
- 平台特定代码
- 版本兼容性处理
- 代码优化开关
- 资源管理宏
2. 动态内存分配
2.1 内存分配基础
#include <stdio.h>
#include <stdlib.h>int main() {// 1. 基本内存分配int *p1 = (int*)malloc(sizeof(int)); // 分配单个整数int *p2 = (int*)calloc(1, sizeof(int)); // 分配并初始化为0int *p3 = (int*)realloc(NULL, sizeof(int)); // 类似malloc// 2. 数组内存分配int *arr = (int*)malloc(5 * sizeof(int));if (arr != NULL) {for (int i = 0; i < 5; i++) {arr[i] = i + 1;}}// 3. 内存释放free(p1);free(p2);free(p3);free(arr);// 4. 避免内存泄漏p1 = p2 = p3 = arr = NULL;return 0;
}
2.2 高级内存管理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 1. 安全的内存分配函数
void* safeMalloc(size_t size) {void* ptr = malloc(size);if (ptr == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}return ptr;
}// 2. 二维数组动态分配
int** create2DArray(int rows, int cols) {int** array = (int**)safeMalloc(rows * sizeof(int*));for (int i = 0; i < rows; i++) {array[i] = (int*)safeMalloc(cols * sizeof(int));}return array;
}// 3. 二维数组释放
void free2DArray(int** array, int rows) {if (array) {for (int i = 0; i < rows; i++) {free(array[i]);}free(array);}
}// 4. 内存池实现
typedef struct MemoryPool {void* memory;size_t size;size_t used;
} MemoryPool;MemoryPool* createMemoryPool(size_t size) {MemoryPool* pool = (MemoryPool*)safeMalloc(sizeof(MemoryPool));pool->memory = safeMalloc(size);pool->size = size;pool->used = 0;return pool;
}void* poolAlloc(MemoryPool* pool, size_t size) {if (pool->used + size > pool->size) {return NULL; // 内存池已满}void* ptr = (char*)pool->memory + pool->used;pool->used += size;return ptr;
}void destroyMemoryPool(MemoryPool* pool) {if (pool) {free(pool->memory);free(pool);}
}int main() {// 1. 二维数组示例int rows = 3, cols = 4;int** matrix = create2DArray(rows, cols);// 初始化矩阵for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {matrix[i][j] = i * cols + j;printf("%2d ", matrix[i][j]);}printf("\n");}// 2. 内存池示例MemoryPool* pool = createMemoryPool(1024); // 1KB内存池// 从内存池分配内存int* numbers = (int*)poolAlloc(pool, 5 * sizeof(int));char* text = (char*)poolAlloc(pool, 20);if (numbers && text) {// 使用分配的内存for (int i = 0; i < 5; i++) {numbers[i] = i;}strcpy(text, "Hello, Memory Pool!");printf("Text: %s\n", text);}// 清理资源free2DArray(matrix, rows);destroyMemoryPool(pool);return 0;
}
2.3 内存管理最佳实践
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 1. 内存跟踪结构
typedef struct {void* ptr;size_t size;const char* file;int line;
} MemoryBlock;#define MAX_BLOCKS 1000
MemoryBlock g_blocks[MAX_BLOCKS];
int g_blockCount = 0;// 2. 内存分配跟踪
#define TRACK_MALLOC(size) trackMalloc(size, __FILE__, __LINE__)
void* trackMalloc(size_t size, const char* file, int line) {void* ptr = malloc(size);if (ptr && g_blockCount < MAX_BLOCKS) {g_blocks[g_blockCount].ptr = ptr;g_blocks[g_blockCount].size = size;g_blocks[g_blockCount].file = file;g_blocks[g_blockCount].line = line;g_blockCount++;}return ptr;
}// 3. 内存释放跟踪
void trackFree(void* ptr) {for (int i = 0; i < g_blockCount; i++) {if (g_blocks[i].ptr == ptr) {memmove(&g_blocks[i], &g_blocks[i + 1], (g_blockCount - i - 1) * sizeof(MemoryBlock));g_blockCount--;break;}}free(ptr);
}// 4. 内存泄漏检测
void checkMemoryLeaks() {if (g_blockCount > 0) {printf("检测到内存泄漏!\n");for (int i = 0; i < g_blockCount; i++) {printf("泄漏: %zu bytes at %p (%s:%d)\n",g_blocks[i].size,g_blocks[i].ptr,g_blocks[i].file,g_blocks[i].line);}} else {printf("未检测到内存泄漏\n");}
}// 5. 对齐内存分配
void* alignedMalloc(size_t size, size_t alignment) {void* ptr = NULL;
#ifdef _WIN32ptr = _aligned_malloc(size, alignment);
#elseif (posix_memalign(&ptr, alignment, size) != 0) {ptr = NULL;}
#endifreturn ptr;
}void alignedFree(void* ptr) {
#ifdef _WIN32_aligned_free(ptr);
#elsefree(ptr);
#endif
}int main() {// 内存跟踪示例int* numbers = (int*)TRACK_MALLOC(5 * sizeof(int));char* text = (char*)TRACK_MALLOC(20);// 使用内存if (numbers && text) {for (int i = 0; i < 5; i++) {numbers[i] = i;}strcpy(text, "Hello");}// 释放部分内存trackFree(numbers);// 检查内存泄漏checkMemoryLeaks(); // 应该显示text未释放// 对齐内存分配示例float* aligned_data = (float*)alignedMalloc(256, 16);if (aligned_data) {// 使用对齐的内存alignedFree(aligned_data);}// 清理剩余资源trackFree(text);checkMemoryLeaks(); // 应该显示无泄漏return 0;
}
【核心要点】
-
内存分配基础:
- malloc/calloc/realloc的使用
- 内存释放和空指针处理
- 内存分配失败处理
- 避免内存泄漏
-
高级内存管理:
- 安全的内存分配封装
- 多维数组的动态分配
- 内存池实现
- 资源管理策略
-
最佳实践:
- 内存跟踪和调试
- 内存泄漏检测
- 内存对齐处理
- 平台兼容性考虑
⚠️ 注意事项:
- 检查内存分配返回值
- 避免内存泄漏和重复释放
- 注意内存对齐要求
- 考虑内存分配效率
- 处理内存碎片问题
🔍 应用场景:
- 动态数组实现
- 图像处理缓冲区
- 数据库缓存管理
- 游戏资源管理
- 大数据处理
3. 位运算
3.1 基本位运算
#include <stdio.h>int main() {// 1. 基本位运算操作unsigned int a = 60; // 二进制:00111100unsigned int b = 13; // 二进制:00001101printf("=== 基本位运算 ===\n");printf("a = %u (%08X)\n", a, a);printf("b = %u (%08X)\n", b, b);printf("按位与 (AND): %u (%08X)\n", a & b, a & b);printf("按位或 (OR): %u (%08X)\n", a | b, a | b);printf("按位异或 (XOR): %u (%08X)\n", a ^ b, a ^ b);printf("按位取反 (NOT): %u (%08X)\n", ~a, ~a);// 2. 移位运算printf("\n=== 移位运算 ===\n");printf("左移2位 (a << 2): %u (%08X)\n", a << 2, a << 2);printf("右移2位 (a >> 2): %u (%08X)\n", a >> 2, a >> 2);// 3. 带符号数右移int negative = -60; // 负数的右移printf("\n=== 带符号右移 ===\n");printf("负数右移1位: %d (%08X)\n", negative >> 1, negative >> 1);return 0;
}
3.2 位运算技巧
#include <stdio.h>// 1. 判断奇偶性
#define IS_EVEN(n) (((n) & 1) == 0)// 2. 交换两数
void swapBits(int *a, int *b) {if (a != b) { // 检查是否为同一地址*a ^= *b;*b ^= *a;*a ^= *b;}
}// 3. 计算二进制中1的个数
int countSetBits(unsigned int n) {int count = 0;while (n) {count += n & 1;n >>= 1;}return count;
}// 4. 快速计算2的幂
#define IS_POWER_OF_TWO(n) (((n) & ((n) - 1)) == 0)// 5. 获取最低位的1
#define LOWEST_SET_BIT(n) ((n) & -(n))int main() {// 1. 奇偶性判断int num = 7;printf("数字 %d 是%s数\n", num, IS_EVEN(num) ? "偶" : "奇");// 2. 位交换示例int x = 5, y = 10;printf("交换前: x = %d, y = %d\n", x, y);swapBits(&x, &y);printf("交换后: x = %d, y = %d\n", x, y);// 3. 计算置位数unsigned int n = 0xFF; // 二进制:11111111printf("数字 %u 中1的个数: %d\n", n, countSetBits(n));// 4. 2的幂检查int powerOfTwo = 16;printf("%d %s 2的幂\n", powerOfTwo, IS_POWER_OF_TWO(powerOfTwo) ? "是" : "不是");// 5. 最低位1int number = 12; // 二进制:1100printf("%d 的最低位1值为: %d\n", number, LOWEST_SET_BIT(number));return 0;
}
3.3 位运算高级应用
#include <stdio.h>// 1. 位域结构
typedef struct {unsigned int ready: 1; // 1位,状态标志unsigned int busy: 1; // 1位,忙标志unsigned int error: 2; // 2位,错误代码unsigned int command: 4; // 4位,命令unsigned int reserved: 24; // 24位,保留
} StatusRegister;// 2. 位掩码操作
#define SET_BIT(x, pos) ((x) |= (1U << (pos)))
#define CLEAR_BIT(x, pos) ((x) &= ~(1U << (pos)))
#define TOGGLE_BIT(x, pos) ((x) ^= (1U << (pos)))
#define GET_BIT(x, pos) (((x) >> (pos)) & 1U)// 3. 循环移位
unsigned int rotateLeft(unsigned int value, int shift) {if ((shift &= sizeof(value) * 8 - 1) == 0)return value;return (value << shift) | (value >> (sizeof(value) * 8 - shift));
}unsigned int rotateRight(unsigned int value, int shift) {if ((shift &= sizeof(value) * 8 - 1) == 0)return value;return (value >> shift) | (value << (sizeof(value) * 8 - shift));
}// 4. 位图实现
#define BITMAP_SIZE 128
typedef struct {unsigned int bits[BITMAP_SIZE / 32];
} Bitmap;void setBit(Bitmap* bitmap, int pos) {bitmap->bits[pos / 32] |= (1U << (pos % 32));
}void clearBit(Bitmap* bitmap, int pos) {bitmap->bits[pos / 32] &= ~(1U << (pos % 32));
}int testBit(Bitmap* bitmap, int pos) {return (bitmap->bits[pos / 32] >> (pos % 32)) & 1U;
}int main() {// 1. 位域示例StatusRegister status = {0};status.ready = 1;status.command = 5;printf("状态寄存器: ready=%d, command=%d\n", status.ready, status.command);// 2. 位掩码操作示例unsigned int flags = 0;SET_BIT(flags, 3); // 设置第3位TOGGLE_BIT(flags, 5); // 切换第5位printf("标志位: %08X\n", flags);printf("第3位: %d\n", GET_BIT(flags, 3));// 3. 循环移位示例unsigned int value = 0x12345678;printf("原值: %08X\n", value);printf("左移4位: %08X\n", rotateLeft(value, 4));printf("右移4位: %08X\n", rotateRight(value, 4));// 4. 位图示例Bitmap bitmap = {0};setBit(&bitmap, 100); // 设置第100位setBit(&bitmap, 101); // 设置第101位printf("第100位: %d\n", testBit(&bitmap, 100));printf("第99位: %d\n", testBit(&bitmap, 99));return 0;
}
【核心要点】
-
基本位运算:
- 按位与、或、异或、取反
- 左移和右移操作
- 带符号数和无符号数的区别
- 位运算优先级
-
位运算技巧:
- 奇偶性判断
- 无临时变量交换
- 计算置位数
- 2的幂检查
- 最低位1提取
-
高级应用:
- 位域结构定义
- 位掩码操作
- 循环移位实现
- 位图数据结构
⚠️ 注意事项:
- 位运算的优先级
- 带符号数右移的符号扩展
- 位域的对齐和移植性
- 位运算的性能考虑
- 代码可读性平衡
🔍 应用场景:
- 状态标志管理
- 权限控制
- 硬件寄存器操作
- 数据压缩
- 加密算法
4. 排序算法
4.1 基本排序算法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 1. 冒泡排序 - 时间复杂度O(n²)
void bubbleSort(int arr[], int n) {int i, j;int flag; // 优化标志for (i = 0; i < n - 1; i++) {flag = 0;for (j = 0; j < n - 1 - i; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;flag = 1;}}if (!flag) break; // 如果一轮未发生交换,说明已经有序}
}// 2. 选择排序 - 时间复杂度O(n²)
void selectionSort(int arr[], int n) {int i, j, min_idx;for (i = 0; i < n - 1; i++) {min_idx = i;for (j = i + 1; j < n; j++) {if (arr[j] < arr[min_idx])min_idx = j;}if (min_idx != i) {int temp = arr[i];arr[i] = arr[min_idx];arr[min_idx] = temp;}}
}// 3. 插入排序 - 时间复杂度O(n²)
void insertionSort(int arr[], int n) {int i, key, j;for (i = 1; i < n; i++) {key = arr[i];j = i - 1;while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
}// 4. 希尔排序 - 时间复杂度O(n^1.3)
void shellSort(int arr[], int n) {for (int gap = n/2; gap > 0; gap /= 2) {for (int i = gap; i < n; i++) {int temp = arr[i];int j;for (j = i; j >= gap && arr[j - gap] > temp; j -= gap)arr[j] = arr[j - gap];arr[j] = temp;}}
}
4.2 高级排序算法
// 1. 快速排序 - 时间复杂度O(nlogn)
void quickSort(int arr[], int low, int high) {if (low < high) {// 三数取中法选择基准int mid = low + (high - low) / 2;if (arr[low] > arr[high])swap(&arr[low], &arr[high]);if (arr[mid] > arr[high])swap(&arr[mid], &arr[high]);if (arr[low] > arr[mid])swap(&arr[low], &arr[mid]);int pivot = arr[mid];int i = low - 1;int j = high + 1;while (1) {do {i++;} while (arr[i] < pivot);do {j--;} while (arr[j] > pivot);if (i >= j)break;swap(&arr[i], &arr[j]);}quickSort(arr, low, j);quickSort(arr, j + 1, high);}
}// 2. 归并排序 - 时间复杂度O(nlogn)
void merge(int arr[], int left, int mid, int right) {int i, j, k;int n1 = mid - left + 1;int n2 = right - mid;int *L = (int*)malloc(n1 * sizeof(int));int *R = (int*)malloc(n2 * sizeof(int));for (i = 0; i < n1; i++)L[i] = arr[left + i];for (j = 0; j < n2; j++)R[j] = arr[mid + 1 + j];i = 0;j = 0;k = left;while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k] = L[i];i++;} else {arr[k] = R[j];j++;}k++;}while (i < n1) {arr[k] = L[i];i++;k++;}while (j < n2) {arr[k] = R[j];j++;k++;}free(L);free(R);
}void mergeSort(int arr[], int left, int right) {if (left < right) {int mid = left + (right - left) / 2;mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}
}// 3. 堆排序 - 时间复杂度O(nlogn)
void heapify(int arr[], int n, int i) {int largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest])largest = left;if (right < n && arr[right] > arr[largest])largest = right;if (largest != i) {swap(&arr[i], &arr[largest]);heapify(arr, n, largest);}
}void heapSort(int arr[], int n) {// 构建最大堆for (int i = n / 2 - 1; i >= 0; i--)heapify(arr, n, i);// 逐个从堆中取出元素for (int i = n - 1; i > 0; i--) {swap(&arr[0], &arr[i]);heapify(arr, i, 0);}
}
4.3 排序算法的比较和应用
// 1. 排序算法性能测试
void testSortingAlgorithm(void (*sortFunc)(int[], int), const char* name, int arr[], int n) {int *testArr = (int*)malloc(n * sizeof(int));memcpy(testArr, arr, n * sizeof(int));clock_t start = clock();sortFunc(testArr, n);clock_t end = clock();double time_spent = (double)(end - start) / CLOCKS_PER_SEC;printf("%s 用时: %f秒\n", name, time_spent);// 验证排序结果int isSorted = 1;for (int i = 1; i < n; i++) {if (testArr[i] < testArr[i-1]) {isSorted = 0;break;}}printf("排序%s\n", isSorted ? "正确" : "错误");free(testArr);
}// 2. 排序算法选择指南
/*
选择排序算法的考虑因素:
1. 数据规模- 小规模数据(n < 50):插入排序- 中等规模(50 <= n < 10000):快速排序- 大规模数据(n >= 10000):归并排序或堆排序2. 稳定性要求- 需要稳定排序:归并排序、插入排序、冒泡排序- 不需要稳定性:快速排序、堆排序、希尔排序3. 空间复杂度要求- O(1)空间:堆排序、快速排序(平均)- O(n)空间:归并排序4. 数据特征- 接近有序:插入排序- 完全随机:快速排序- 大量重复元素:三路快排
*/// 3. 实际应用示例
void sortExample() {// 生成测试数据int n = 10000;int *arr = (int*)malloc(n * sizeof(int));for (int i = 0; i < n; i++)arr[i] = rand() % 10000;// 测试各种排序算法printf("=== 排序算法性能比较 ===\n");testSortingAlgorithm(bubbleSort, "冒泡排序", arr, n);testSortingAlgorithm(insertionSort, "插入排序", arr, n);testSortingAlgorithm(quickSort, "快速排序", arr, n);testSortingAlgorithm(mergeSort, "归并排序", arr, n);testSortingAlgorithm(heapSort, "堆排序", arr, n);free(arr);
}
【核心要点】
-
基本排序算法:
- 冒泡排序:相邻元素比较交换
- 选择排序:每次选择最小元素
- 插入排序:构建有序序列
- 希尔排序:改进的插入排序
-
高级排序算法:
- 快速排序:分治法,基准选择关键
- 归并排序:分治法,稳定排序
- 堆排序:利用堆数据结构
-
算法选择:
- 考虑数据规模
- 考虑稳定性要求
- 考虑空间复杂度
- 考虑数据特征
⚠️ 注意事项:
- 基准元素的选择对快排性能影响大
- 递归深度可能导致栈溢出
- 原地排序vs额外空间
- 稳定性的保证
- 特殊输入的处理
5. 查找算法
5.1 静态查找
#include <stdio.h>// 1. 顺序查找
int sequentialSearch(int arr[], int n, int target) {for (int i = 0; i < n; i++) {if (arr[i] == target)return i;}return -1;
}// 2. 二分查找(迭代版本)
int binarySearch(int arr[], int n, int target) {int left = 0;int right = n - 1;while (left <= right) {int mid = left + (right - left) / 2; // 防止溢出if (arr[mid] == target)return mid;if (arr[mid] < target)left = mid + 1;elseright = mid - 1;}return -1;
}// 3. 二分查找(递归版本)
int binarySearchRecursive(int arr[], int left, int right, int target) {if (right >= left) {int mid = left + (right - left) / 2;if (arr[mid] == target)return mid;if (arr[mid] > target)return binarySearchRecursive(arr, left, mid - 1, target);return binarySearchRecursive(arr, mid + 1, right, target);}return -1;
}// 4. 插值查找
int interpolationSearch(int arr[], int n, int target) {int left = 0;int right = n - 1;while (left <= right && target >= arr[left] && target <= arr[right]) {if (left == right) {if (arr[left] == target) return left;return -1;}// 使用插值公式计算探测点int pos = left + (((double)(right - left) * (target - arr[left])) / (arr[right] - arr[left]));if (arr[pos] == target)return pos;if (arr[pos] < target)left = pos + 1;elseright = pos - 1;}return -1;
}
5.2 动态查找
// 1. 二叉搜索树节点定义
typedef struct TreeNode {int key;struct TreeNode *left;struct TreeNode *right;
} TreeNode;// 创建新节点
TreeNode* createNode(int key) {TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));node->key = key;node->left = node->right = NULL;return node;
}// 插入节点
TreeNode* insert(TreeNode* root, int key) {if (root == NULL)return createNode(key);if (key < root->key)root->left = insert(root->left, key);else if (key > root->key)root->right = insert(root->right, key);return root;
}// 查找节点
TreeNode* search(TreeNode* root, int key) {if (root == NULL || root->key == key)return root;if (key < root->key)return search(root->left, key);return search(root->right, key);
}// 2. 平衡二叉树(AVL树)节点
typedef struct AVLNode {int key;struct AVLNode *left;struct AVLNode *right;int height;
} AVLNode;// 获取节点高度
int height(AVLNode *N) {if (N == NULL)return 0;return N->height;
}// 获取平衡因子
int getBalance(AVLNode *N) {if (N == NULL)return 0;return height(N->left) - height(N->right);
}// AVL树的插入和查找操作(略)
5.3 哈希查找
#define TABLE_SIZE 10// 1. 链地址法
typedef struct HashNode {int key;int value;struct HashNode* next;
} HashNode;typedef struct {HashNode* table[TABLE_SIZE];
} HashMap;// 初始化哈希表
void initHashMap(HashMap* map) {for (int i = 0; i < TABLE_SIZE; i++)map->table[i] = NULL;
}// 哈希函数
int hash(int key) {return key % TABLE_SIZE;
}// 插入键值对
void insert(HashMap* map, int key, int value) {int index = hash(key);HashNode* newNode = (HashNode*)malloc(sizeof(HashNode));newNode->key = key;newNode->value = value;newNode->next = map->table[index];map->table[index] = newNode;
}// 查找值
HashNode* search(HashMap* map, int key) {int index = hash(key);HashNode* current = map->table[index];while (current != NULL) {if (current->key == key)return current;current = current->next;}return NULL;
}// 2. 开放地址法
typedef struct {int key;int value;int isOccupied;
} HashEntry;typedef struct {HashEntry* table;int size;
} HashTable;// 初始化开放地址哈希表
HashTable* createHashTable(int size) {HashTable* ht = (HashTable*)malloc(sizeof(HashTable));ht->table = (HashEntry*)calloc(size, sizeof(HashEntry));ht->size = size;return ht;
}// 线性探测
int linearProbe(HashTable* ht, int key) {int index = hash(key);int i = 0;while (ht->table[(index + i) % ht->size].isOccupied && ht->table[(index + i) % ht->size].key != key) {i++;if (i == ht->size) return -1; // 表满}return (index + i) % ht->size;
}
【核心要点】
-
静态查找:
- 顺序查找:O(n),适用于小规模无序数据
- 二分查找:O(logn),要求有序数据
- 插值查找:O(loglogn),适用于均匀分布数据
-
动态查找:
- 二叉搜索树:平均O(logn),最差O(n)
- AVL树:严格O(logn),自平衡
- 红黑树:O(logn),实际应用广泛
-
哈希查找:
- 链地址法:处理冲突,适用于动态数据
- 开放地址法:节省空间,适用于静态数据
- 时间复杂度:平均O(1)
⚠️ 注意事项:
-
选择合适的查找算法要考虑:
- 数据规模
- 数据是否有序
- 数据分布特征
- 空间限制
- 查找频率
-
实现细节:
- 边界条件处理
- 溢出处理
- 错误处理
- 资源管理
🔍 应用场景:
-
静态查找:
- 有序数组查找
- 数据库索引
- 文件检索
-
动态查找:
- 符号表实现
- 路由表查找
- 文件系统
-
哈希查找:
- 缓存系统
- 数据库索引
- 字符串匹配
6. 字符串算法
6.1 字符串反转
void reverseString(char* str) {int length = strlen(str);for (int i = 0; i < length/2; i++) {char temp = str[i];str[i] = str[length-1-i];str[length-1-i] = temp;}
}
6.2 KMP字符串匹配
void computeLPSArray(char* pattern, int M, int* lps) {int len = 0;lps[0] = 0;int i = 1;while (i < M) {if (pattern[i] == pattern[len]) {len++;lps[i] = len;i++;} else {if (len != 0) {len = lps[len-1];} else {lps[i] = 0;i++;}}}
}int KMPSearch(char* text, char* pattern) {int M = strlen(pattern);int N = strlen(text);int lps[M];computeLPSArray(pattern, M, lps);int i = 0;int j = 0;while (i < N) {if (pattern[j] == text[i]) {j++;i++;}if (j == M) {return i-j; // 找到匹配} else if (i < N && pattern[j] != text[i]) {if (j != 0)j = lps[j-1];elsei++;}}return -1; // 未找到匹配
}
7. 数学算法
7.1 最大公约数(GCD)
int gcd(int a, int b) {if (b == 0)return a;return gcd(b, a % b);
}// 扩展欧几里得算法
void extendedGCD(int a, int b, int *x, int *y) {if (b == 0) {*x = 1;*y = 0;return;}int x1, y1;extendedGCD(b, a % b, &x1, &y1);*x = y1;*y = x1 - (a/b) * y1;
}
7.2 素数筛选(埃氏筛)
void sieveOfEratosthenes(int n) {int prime[n+1];memset(prime, 1, sizeof(prime));for (int p = 2; p * p <= n; p++) {if (prime[p]) {// 将p的倍数标记为非素数for (int i = p * p; i <= n; i += p)prime[i] = 0;}}// 打印素数for (int p = 2; p <= n; p++)if (prime[p])printf("%d ", p);
}
8. 图算法
8.1 深度优先搜索(DFS)
#define MAX_VERTICES 100void DFS(int graph[MAX_VERTICES][MAX_VERTICES], int vertex, int visited[], int V) {printf("%d ", vertex);visited[vertex] = 1;for (int i = 0; i < V; i++) {if (graph[vertex][i] && !visited[i]) {DFS(graph, i, visited, V);}}
}
8.2 广度优先搜索(BFS)
void BFS(int graph[MAX_VERTICES][MAX_VERTICES], int start, int V) {int visited[MAX_VERTICES] = {0};int queue[MAX_VERTICES];int front = 0, rear = 0;visited[start] = 1;queue[rear++] = start;while (front < rear) {int vertex = queue[front++];printf("%d ", vertex);for (int i = 0; i < V; i++) {if (graph[vertex][i] && !visited[i]) {visited[i] = 1;queue[rear++] = i;}}}
}
9. 动态规划
9.1 斐波那契数列
int fibonacci(int n) {if (n <= 1) return n;int dp[n+1];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= n; i++)dp[i] = dp[i-1] + dp[i-2];return dp[n];
}
9.2 背包问题
int knapsack(int W, int wt[], int val[], int n) {int dp[n+1][W+1];for (int i = 0; i <= n; i++) {for (int w = 0; w <= W; w++) {if (i == 0 || w == 0)dp[i][w] = 0;else if (wt[i-1] <= w)dp[i][w] = max(val[i-1] + dp[i-1][w-wt[i-1]], dp[i-1][w]);elsedp[i][w] = dp[i-1][w];}}return dp[n][W];
}
以上就是全部的内容。如果各位大佬有疑问,欢迎在评论区留言,你的点赞收藏是我创作的动力!