C语言实战高频深度错误解析
文章目录
- C语言实战高频深度错误解析
- [一、第12课 跨平台与异常处理误区](#一、第12课 跨平台与异常处理误区)
-
- [1.1 课程基础信息](#1.1 课程基础信息)
- [1.2 课程核心目标](#1.2 课程核心目标)
- [1.3 课程核心内容拆解](#1.3 课程核心内容拆解)
-
- [1.3.1 模块一:跨平台开发的核心误区与规避方法](#1.3.1 模块一:跨平台开发的核心误区与规避方法)
-
- [1. 大小端依赖导致的跨平台错误(最高频)](#1. 大小端依赖导致的跨平台错误(最高频))
- [2. 数据类型与编译器差异导致的跨平台陷阱](#2. 数据类型与编译器差异导致的跨平台陷阱)
- [1.3.2 模块二:setjmp/longjmp异常处理的陷阱](#1.3.2 模块二:setjmp/longjmp异常处理的陷阱)
- [1.3.3 模块三:编译器警告与信号处理基础](#1.3.3 模块三:编译器警告与信号处理基础)
-
- [1. 编译器警告的重要性(被忽视的隐藏bug)](#1. 编译器警告的重要性(被忽视的隐藏bug))
- [2. 信号处理基础(避免程序异常终止)](#2. 信号处理基础(避免程序异常终止))
- [1.4 课堂实操环节(25分钟)](#1.4 课堂实操环节(25分钟))
-
- [1.4.1 实操案例1:跨平台字节序转换与数据解析](#1.4.1 实操案例1:跨平台字节序转换与数据解析)
- [1.4.2 实操案例2:setjmp/longjmp异常处理与资源清理](#1.4.2 实操案例2:setjmp/longjmp异常处理与资源清理)
- [1.4.3 实操案例3:综合排查跨平台与异常处理隐藏bug](#1.4.3 实操案例3:综合排查跨平台与异常处理隐藏bug)
- [1.5 课后作业](#1.5 课后作业)
- [1.6 课程总结](#1.6 课程总结)
- [二、上一课作业答案 宏定义与位运算陷阱 实战作业代码](#二、上一课作业答案 宏定义与位运算陷阱 实战作业代码)
-
- [2.1 代码功能说明](#2.1 代码功能说明)
- [2.2 完整实战代码](#2.2 完整实战代码)
- [2.3 代码运行注意事项](#2.3 代码运行注意事项)
- 结课语:课程回顾总结
一、第12课 跨平台与异常处理误区
1.1 课程基础信息
| 项目 | 详情 |
|---|---|
| 课程标题 | 跨平台与异常处理误区 |
| 课时时长 | 45分钟(理论20分钟 + 实操25分钟) |
| 前置知识 | C语言基础语法、宏定义、位运算、文件操作、基本错误处理思路 |
| 课程环节 | 错误案例→根源分析→规避方法→实操修正 |
1.2 课程核心目标
-
掌握跨平台开发的核心误区(大小端、数据类型、编译器差异),理解底层原理并掌握规避技巧,提升代码跨平台兼容性。
-
掌握setjmp/longjmp异常处理的使用陷阱,明确其适用场景与风险,避免因异常处理不当导致的资源泄漏等问题。
-
重视编译器警告的作用,掌握常用编译警告参数的使用,养成规范编译、及时排查潜在问题的习惯。
-
了解信号处理基础,掌握避免程序异常终止的核心方法,能独立排查跨平台与异常处理相关的隐藏bug,提升代码健壮性。
-
通过综合实操,整合前11节课核心内容,建立"预防为先、规范编码"的工业级开发思维。
1.3 课程核心内容拆解
1.3.1 模块一:跨平台开发的核心误区与规避方法
本模块聚焦跨平台开发高频坑点,直接影响代码可移植性,讲解时长8分钟,是嵌入式、多环境开发的核心重点。
1. 大小端依赖导致的跨平台错误(最高频)
错误场景:直接通过指针操作解析多字节数据(如int、float),未考虑不同CPU架构的大小端差异,导致跨平台数据解析错误,常见于嵌入式设备、PC端与服务器的数据交互场景。
核心概念:
-
大端(Big-Endian):高位字节存低地址,低位字节存高地址(如网络字节序)。
-
小端(Little-Endian):低位字节存低地址,高位字节存高地址(如x86、x86_64架构CPU)。
错误代码示例
C
#include <stdio.h>
#include <stdint.h>
// 错误:直接通过指针解析多字节数据,依赖大小端
int main() {
uint8_t buf[4] = {0x12, 0x34, 0x56, 0x78};
uint32_t *p = (uint32_t*)buf;
// 小端架构(x86)结果:0x78563412,大端架构结果:0x12345678,跨平台结果不一致
printf("解析结果:0x%x\n", *p);
return 0;
}
根源分析:不同CPU架构的字节序定义不同,直接通过指针强制转换解析多字节数据,会依赖当前平台的大小端,导致跨平台数据解析异常,属于典型的平台相关代码。
正确写法(跨平台兼容)
C
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h> // 包含htonl、ntohl等字节序转换函数
// 方法1:使用字节序转换函数(推荐,适用于网络传输、跨平台数据交互)
uint32_t buf_to_uint32(const uint8_t *buf) {
// htonl:主机字节序→网络字节序(大端);ntohl:网络字节序→主机字节序
return ntohl(*(uint32_t*)buf);
}
// 方法2:手动拼接字节(兼容性最强,无依赖)
uint32_t buf_to_uint32_manual(const uint8_t *buf) {
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
int main() {
uint8_t buf[4] = {0x12, 0x34, 0x56, 0x78};
printf("解析结果(字节序转换):0x%x\n", buf_to_uint32(buf));
printf("解析结果(手动拼接):0x%x\n", buf_to_uint32_manual(buf));
return 0;
}
2. 数据类型与编译器差异导致的跨平台陷阱
高频错误场景:
-
使用int、long等不定长数据类型(C标准未规定其具体位数),不同编译器、不同架构(32位/64位)下长度不同,导致数值溢出或逻辑错误。
-
忽略编译器扩展语法(如GCC的__attribute__、MSVC的__declspec),导致代码仅能在特定编译器下编译通过,无法跨编译器移植。
-
浮点型数据精度差异,不同编译器对float、double的存储精度处理不同,导致跨平台数值计算偏差。
错误代码示例
C
#include <stdio.h>
// 错误1:使用long不定长类型,32位系统long为4字节,64位系统为8字节
long get_system_value() {
return 0x100000000L; // 32位系统下溢出,64位系统正常
}
// 错误2:使用GCC扩展语法,MSVC编译器无法编译
__attribute__((packed)) typedef struct {
uint8_t a;
uint32_t b;
} TestStruct;
int main() {
printf("long类型长度:%ld字节\n", sizeof(long));
printf("结构体长度:%ld字节\n", sizeof(TestStruct));
return 0;
}
规避技巧:
-
优先使用定长数据类型(uint8_t、uint16_t、uint32_t、int32_t等),来自stdint.h头文件,保证跨平台长度一致。
-
避免使用编译器扩展语法,如需使用,通过宏定义做条件编译,适配不同编译器。
-
浮点型计算如需高精度,优先使用double,避免依赖float的精度,跨平台计算前做好精度校准。
1.3.2 模块二:setjmp/longjmp异常处理的陷阱
本模块讲解异常处理的风险点,避免因不当使用导致的资源泄漏,讲解时长5分钟。
核心概念:setjmp/longjmp是C语言中用于异常跳转的函数,setjmp保存当前程序上下文(寄存器、程序计数器等),longjmp恢复上下文并跳转到setjmp保存的位置,替代try-catch(C语言无原生异常机制)。
错误场景:使用longjmp跳转时,跳过了资源释放(如文件关闭、内存释放)、变量初始化等操作,导致资源泄漏、数据异常。
错误代码示例
C
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
void error_handler() {
// 跳转回setjmp位置,跳过了file和ptr的释放操作
longjmp(env, 1);
}
int main() {
FILE *file = NULL;
int *ptr = NULL;
// 保存上下文,返回0表示首次调用,非0表示longjmp跳转过来
if (setjmp(env) != 0) {
printf("异常处理完成\n");
return 0;
}
// 打开文件、分配内存
file = fopen("test.txt", "r");
ptr = (int*)malloc(1024);
if (file == NULL || ptr == NULL) {
error_handler(); // 异常时跳转
}
// 正常逻辑:释放资源
fclose(file);
free(ptr);
return 0;
}
根源分析:longjmp会直接跳转,跳过跳转点之间的所有代码,若跳转点之间有资源分配、变量初始化等操作,会导致资源无法释放,引发内存泄漏、文件描述符泄漏等问题。
正确写法(避免资源泄漏)
C
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
FILE *file = NULL;
int *ptr = NULL;
// 资源清理函数,无论正常还是异常,都能释放资源
void clean_resource() {
if (file != NULL) {
fclose(file);
file = NULL;
}
if (ptr != NULL) {
free(ptr);
ptr = NULL;
}
}
void error_handler() {
clean_resource(); // 跳转前先清理资源
longjmp(env, 1);
}
int main() {
if (setjmp(env) != 0) {
printf("异常处理完成,资源已清理\n");
return 0;
}
file = fopen("test.txt", "r");
ptr = (int*)malloc(1024);
if (file == NULL || ptr == NULL) {
error_handler();
}
// 正常逻辑:清理资源
clean_resource();
printf("正常执行完成\n");
return 0;
}
1.3.3 模块三:编译器警告与信号处理基础
本模块聚焦"预防式开发",减少隐藏bug,讲解时长7分钟。
1. 编译器警告的重要性(被忽视的隐藏bug)
错误场景:编译时忽略编译器警告(如未初始化变量、类型不匹配、隐式转换),认为"警告不影响运行",最终导致运行时崩溃或逻辑错误,这是工业级开发中高频低阶错误。
核心知识点:
-
-Wall:开启所有常见警告(推荐默认开启)。
-
-Werror:将所有警告视为错误,强制开发者修正所有潜在问题(工业级开发必用)。
错误示例与修正
C
// 错误代码(编译警告:未初始化变量a)
#include <stdio.h>
int main() {
int a;
printf("a的值:%d\n", a); // 未初始化,值为随机值,运行时异常
return 0;
}
// 编译命令(忽略警告):gcc test.c -o test → 有警告,可运行但结果不可控
// 编译命令(强制修正警告):gcc test.c -o test -Wall -Werror → 警告转为错误,无法编译
// 正确代码(初始化变量)
#include <stdio.h>
int main() {
int a = 0; // 初始化
printf("a的值:%d\n", a);
return 0;
}
2. 信号处理基础(避免程序异常终止)
高频场景:程序运行时遇到信号(如Ctrl+C、段错误、非法指令),未做处理会直接异常终止,影响程序稳定性,尤其在嵌入式、后台程序中。
正确示例(信号捕获与处理)
C
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
// 信号处理函数
void signal_handler(int signum) {
// 根据信号类型做对应处理
switch (signum) {
case SIGINT: // Ctrl+C触发的信号
printf("\n捕获到SIGINT信号(Ctrl+C),程序即将退出\n");
// 此处可添加资源清理逻辑
exit(0);
break;
case SIGSEGV: // 段错误信号(如空指针解引用)
printf("捕获到SIGSEGV信号(段错误),程序异常退出\n");
exit(1);
break;
default:
printf("捕获到未知信号:%d\n", signum);
}
}
int main() {
// 注册信号处理函数
signal(SIGINT, signal_handler);
signal(SIGSEGV, signal_handler);
printf("程序运行中,按Ctrl+C测试信号处理...\n");
while (1) {
sleep(1); // 循环等待信号
}
return 0;
}
1.4 课堂实操环节(25分钟)
1.4.1 实操案例1:跨平台字节序转换与数据解析
任务:修正以下跨平台数据解析代码的错误,实现大小端兼容,确保在32位/64位、小端/大端架构下解析结果一致,验证运行效果。
C
#include <stdio.h>
// 错误代码:依赖大小端,跨平台解析异常
int main() {
uint8_t data_buf[4] = {0x00, 0x00, 0x00, 0x01};
uint32_t value = *(uint32_t*)data_buf;
printf("解析的值:%d\n", value);
return 0;
}
实操要求:
-
分析错误根源,说明大小端对解析结果的影响。
-
使用两种方法(字节序转换函数、手动拼接)修正代码,确保跨平台兼容。
-
编译运行,验证不同架构下(或模拟大小端)解析结果一致。
1.4.2 实操案例2:setjmp/longjmp异常处理与资源清理
任务:找出以下代码中的异常处理陷阱,修复资源泄漏问题,确保无论正常执行还是异常跳转,都能正确释放资源。
C
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
void error() {
longjmp(env, 1);
}
int main() {
int *ptr = (int*)malloc(1024);
FILE *fp = fopen("test.txt", "w");
if (setjmp(env) != 0) {
printf("异常处理\n");
return 0;
}
if (ptr == NULL || fp == NULL) {
error();
}
fprintf(fp, "test");
free(ptr);
fclose(fp);
return 0;
}
实操要求:
-
标注代码中的资源泄漏点,说明错误根源。
-
添加资源清理逻辑,修复异常跳转导致的泄漏问题。
-
测试正常执行和异常跳转两种场景,验证资源是否正确释放。
1.4.3 实操案例3:综合排查跨平台与异常处理隐藏bug
任务:分析以下综合代码,找出跨平台、异常处理、编译器警告相关的所有错误,修正后确保代码可跨编译器、跨架构运行,无警告、无泄漏。
C
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
long value;
void handle_err() {
longjmp(env, 1);
}
int main() {
uint8_t buf[2] = {0x01, 0x02};
value = *(long*)buf;
FILE *fp = fopen("test.txt", "r");
if (setjmp(env) != 0) {
printf("异常退出\n");
return 0;
}
if (fp == NULL) {
handle_err();
}
fclose(fp);
printf("value: %ld\n", value);
return 0;
}
实操要求:
-
找出所有错误(至少3处),分别说明错误类型与根源。
-
按工业级规范修正代码,开启-Wall -Werror编译无警告。
-
验证跨平台兼容性(重点关注数据解析)和异常处理的资源清理。
1.5 课后作业
-
编写一个跨平台的多字节数据解析函数,支持uint16_t、uint32_t、uint64_t三种类型,适配大小端架构,要求用两种方法实现(字节序转换、手动拼接),并编写测试代码验证。
-
修复以下代码的所有错误,确保无编译器警告、无资源泄漏、支持跨编译器编译,说明每处错误的根源及修正思路:
`#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
void err() {
longjmp(env, 1);
}
int main() {
int a;
char *buf = malloc(100);
FILE *fp = fopen("test.txt", "w");
if (setjmp(env) != 0) {
printf("err\n");
return 0;
}
if (fp == NULL) err();
fprintf(fp, "hello");
printf("a: %d\n", a);
free(buf);
fclose(fp);
return 0;
}
`
-
简述跨平台开发的3个核心误区,以及setjmp/longjmp的2个主要陷阱,结合实际开发场景举例说明规避方法。
-
编写一个信号处理程序,捕获SIGINT(Ctrl+C)、SIGSEGV(段错误)、SIGTERM(终止信号),实现信号捕获后的资源清理与优雅退出。
1.6 课程总结
-
跨平台开发的核心是"屏蔽平台差异",重点规避大小端、不定长数据类型、编译器扩展语法三大误区,优先使用定长类型和跨平台函数,确保代码可移植性。
-
setjmp/longjmp的核心陷阱是"跳转跳过资源清理",使用时必须提前规划资源释放逻辑,通过统一清理函数,避免内存泄漏、文件描述符泄漏等问题。
-
编译器警告是隐藏bug的"预警信号",工业级开发必须开启-Wall、-Werror,养成"无警告编译"的习惯,从源头规避低阶错误。
-
信号处理的核心是"优雅应对异常",通过注册信号处理函数,捕获常见异常信号,实现资源清理与优雅退出,提升程序稳定性。
-
本课程是前11节课的综合复盘,核心思维是"预防为先、规范编码",工业级C语言开发的关键的是从编码源头规避未定义行为和平台依赖,提升代码健壮性与可维护性。
二、上一课作业答案 宏定义与位运算陷阱 实战作业代码
2.1 代码功能说明
本代码为Linux/Windows跨平台C语言实战代码,紧扣第11课"宏定义与位运算陷阱"核心知识点,实现四大核心功能:一是复现宏定义未加括号、参数副作用、多行宏未封装的典型错误并修复;二是验证位运算的位移未定义行为、优先级错误及跨平台陷阱;三是演示全局变量滥用的危害及合规替代方案;四是封装工业级位操作宏,规避所有高频陷阱。代码内置错误标注、修复前后对比及日志打印,学员可直观理解宏定义与位运算的核心坑点,掌握排查与修正方法,提升代码健壮性。
2.2 完整实战代码
C
#include <stdio.h>
#include <stdint.h>
// 一、宏定义错误复现与修复
// 错误宏定义(未加括号、参数副作用、多行未封装)
#define MUL_ERR(x,y) x*y // 未加括号,优先级陷阱
#define MAX_ERR(a,b) a>b?a:b // 未加括号,优先级陷阱
#define SWAP_ERR(a,b) int temp=a;a=b;b=temp; // 多行未封装,分支逻辑错误
#define SQUARE_ERR(x) ((x)*(x)) // 无括号问题,但存在参数副作用陷阱
// 正确宏定义(工业级规范)
#define MUL(x,y) ((x)*(y)) // 双括号原则
#define MAX(a,b) ((a)>(b)?(a):(b)) // 双括号原则
#define SWAP(a,b) do{ \ // do-while封装多行宏
int temp = (a); \
(a) = (b); \
(b) = temp; \
}while(0)
#define SQUARE(x) ((x)*(x)) // 双括号,禁止传入副作用参数
// 二、工业级位操作宏(支持8/16/32/64位无符号类型,规避跨平台陷阱)
#define SET_BIT(val, bit) ((val) |= (1ULL << (bit)))
#define CLR_BIT(val, bit) ((val) &= ~(1ULL << (bit)))
#define TOG_BIT(val, bit) ((val) ^= (1ULL << (bit)))
#define CHECK_BIT(val, bit) (((val) >> (bit)) & 1ULL)
// 三、全局变量滥用修复(用局部变量+函数参数替代全局变量)
// 错误:全局变量导致重入性问题
int sum_err = 0;
int calculate_sum_err(int* arr, int len) {
sum_err = 0;
for (int i = 0; i < len; i++) {
sum_err += arr[i];
}
return sum_err;
}
// 正确:无全局变量,保证函数可重入性
int calculate_sum(int* arr, int len) {
int sum = 0; // 局部变量
for (int i = 0; i < len; i++) {
sum += arr[i];
}
return sum;
}
// 四、位运算错误复现与修复
void test_bit_operation() {
printf("===== 位运算错误复现与修复 =====\n");
// 错误1:有符号数左移溢出(未定义行为)
int8_t val_err = 0x40;
val_err <<= 2; // 溢出,未定义行为
printf("有符号数左移溢出(错误):%d\n", val_err);
// 正确1:使用无符号定长类型,避免溢出
uint8_t val = 0x40;
if (val <= (0xFF >> 2)) { // 校验是否溢出
val <<= 2;
}
printf("无符号数左移(正确):%d\n", val);
// 错误2:位移位数超出类型位宽(未定义行为)
uint32_t data_err = 1;
data_err <<= 32; // 超出32位,未定义行为
printf("位移超出位宽(错误):%u\n", data_err);
// 正确2:校验位移位数
uint32_t data = 1;
if (31 < 32) { // 位移位数必须小于类型位宽
data <<= 31;
}
printf("位移校验后(正确):%u\n", data);
// 正确位操作宏测试
uint64_t bit_val = 0;
SET_BIT(bit_val, 10);
printf("置位bit10后:%llu\n", bit_val);
CLR_BIT(bit_val, 10);
printf("清零bit10后:%llu\n", bit_val);
}
int main() {
// 1. 宏定义错误测试与修复
printf("===== 宏定义错误测试与修复 =====\n");
int a = 3, b = 2, c = 4;
printf("MUL_ERR(a+b, c) = %d(错误,预期20,实际11)\n", MUL_ERR(a+b, c));
printf("MUL(a+b, c) = %d(正确)\n", MUL(a+b, c));
int x = 1, y = 2;
if (x < y)
SWAP_ERR(x,y); // 编译报错(else无匹配if)
SWAP(x,y); // 正确使用
printf("SWAP后 x=%d, y=%d\n", x, y);
// 2. 宏参数副作用测试
printf("\n===== 宏参数副作用测试 =====\n");
int num = 3;
printf("SQUARE_ERR(num++) = %d(错误,num自增2次)\n", SQUARE_ERR(num++));
printf("num最终值:%d\n", num);
num = 3;
int temp = num++; // 先计算,再传入宏
printf("SQUARE(temp) = %d(正确,num自增1次)\n", SQUARE(temp));
printf("num最终值:%d\n", num);
// 3. 全局变量修复测试
printf("\n===== 全局变量修复测试 =====\n");
int arr[] = {1,2,3,4,5};
printf("calculate_sum_err结果:%d\n", calculate_sum_err(arr, 5));
printf("calculate_sum结果:%d\n", calculate_sum(arr, 5));
// 4. 位运算测试
test_bit_operation();
printf("\n===== 所有测试完成 =====\n");
return 0;
}
2.3 代码运行注意事项
-
编译运行环境 :支持Linux、Windows、macOS跨平台编译,Windows需使用MinGW或MSVC编译器,Linux/macOS使用gcc编译;编译命令:
gcc macro_bit_test.c -o macro_bit_test -Wall -Werror,Windows编译后直接运行.exe文件,Linux/macOS运行命令:./macro_bit_test。 -
核心知识点对应:代码完全匹配第11课核心内容,重点对应宏定义双括号原则、do-while封装多行宏、宏参数副作用规避,位运算无符号定长类型使用、位移未定义行为校验,全局变量滥用修复三大核心模块。
-
安全规范:代码开启-Wall -Werror编译,无任何警告;位操作宏使用1ULL后缀,适配64位类型,规避跨平台陷阱;函数无全局变量,保证可重入性,符合工业级开发规范。
-
高频坑点提醒:严禁给宏传入自增/自减、函数调用等有副作用的参数;位移运算必须校验位移位数,避免超出类型位宽;多行宏必须用do-while(0)封装,避免分支逻辑错误;全局变量非特殊场景严禁使用。
-
拓展测试建议:可修改宏参数为自增/自减,观察副作用影响;修改位移位数,验证未定义行为的表现;尝试在多线程环境下调用calculate_sum_err和calculate_sum,观察全局变量导致的重入性问题。
结课语:课程回顾总结
本次课程聚焦C语言实战中宏定义与位运算、跨平台与异常处理两大模块的高频深度错误,是提升代码健壮性、适配工业级开发的核心内容。第11课重点讲解宏定义与位运算的陷阱,核心围绕宏定义的纯文本替换特性,剖析了未加括号导致的优先级错误、参数副作用、多行宏未封装的逻辑问题,明确了双括号原则、do-while封装等工业级规范;同时针对位运算的未定义行为、优先级混淆、跨平台兼容性问题,提出了优先使用无符号定长类型、严格校验位移位数的规避方法,还强调了全局变量滥用的危害与合规使用场景,通过实操案例强化了错误排查与修正能力。
第12课作为综合复盘课,聚焦跨平台开发与异常处理误区,重点解决大小端依赖、不定长数据类型、编译器差异导致的跨平台问题,通过字节序转换函数、手动拼接等方法实现代码可移植性;针对setjmp/longjmp异常跳转的陷阱,提出了统一资源清理的解决方案,避免资源泄漏;强调了编译器警告的重要性,推荐开启-Wall、-Werror强制修正潜在问题,同时讲解了信号处理基础,实现程序异常时的优雅退出。
课程始终贯穿"错误案例→根源分析→规避方法→实操修正"的思路,核心思维是"预防为先、规范编码"。通过实战作业代码与综合实操,学员可熟练掌握核心陷阱的规避技巧,建立工业级开发思维,减少实战中的bug,提升代码的健壮性、可维护性与可移植性,为后续嵌入式、多线程等复杂场景开发奠定坚实基础。
上一课链接: C语言逆向学习基础课 第 11 课:宏定义与位运算陷阱详解
第一课课程: C语言逆向学习基础课 第1课:数组越界与指针操作基础陷阱