暑期自学嵌入式——Day08(C语言阶段)
接续上文:暑期自学嵌入式——Day07(C语言阶段)-CSDN博客
点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
主页:
一位搞嵌入式的 genius-CSDN博客一位搞嵌入式的 genius擅长前后端项目开发,微机原理与接口技术,嵌入式自学专栏,等方面的知识,一位搞嵌入式的 genius关注matlab,论文阅读,前端框架,stm32,c++,node.js,c语言,智能家居,vue.js,html,npm,单片机领域.https://blog.csdn.net/m0_73589512?spm=1000.2115.3001.5343
目录
Day08
1. 函数:C 语言的模块化核心
一、函数的基本概念与结构
1. 核心定义:独立功能模块
2. 四要素(函数的 “身份证”)
3. 语法结构
二、函数的声明与调用
1. 声明:告诉编译器函数 “长什么样”
2. 调用:执行函数功能
三、关键概念:形参、实参与返回值
1. 形参 vs 实参(参数传递的核心)
2. 返回值:函数的输出
四、库函数与头文件
1. 头文件的作用
2. 常见库函数与对应头文件
五、常见错误与注意事项
六、知识小结
七、实践建议
2. 函数参数传递:从值传递到地址传递的全面解析
一、函数参数传递的三种方式
1. 全局变量传递(不推荐)
2. 复制传递(值传递):形参是实参的 “副本”
3. 地址传递(指针传递):通过指针修改实参
二、三种传递方式的对比
三、const 修饰符:保护数据不被修改
1. 常见用法:
四、常见错误与注意事项
五、知识小结
六、实践建议
3. 数组的传参方法:从一维数组到字符串的实战解析
一、数组传参的两种核心方式
1. 地址传递(默认方式,推荐)
2. 复制传递(不推荐,仅特殊场景使用)
二、一维数组传参的核心问题:元素个数的传递
1. 典型错误:在函数内用sizeof计算长度
2. 正确做法:主函数计算长度并传递
三、字符串(字符数组)的传参特殊性
1. 案例:删除字符串中的空格(双指针法)
2. 关键技巧:双指针法
四、数组与字符串传参的对比
五、常见错误与注意事项
六、知识小结
七、实践建议
4. 指针函数 1 :内存管理的核心战场
一、指针函数的本质与致命陷阱
二、四种合法的返回值类型
1. 全局变量地址
2. 静态变量地址(推荐)
3. 字符串常量地址(只读场景)
4. 动态分配的内存(灵活但需谨慎)
三、内存管理的黄金法则
四、实战案例:字符串处理函数设计
1. 错误案例:返回局部数组
2. 正确方案:静态变量
3. 正确方案:动态内存
五、常见错误与防御性编程
六、知识小结
七、实践建议
Day08
1. 函数:C 语言的模块化核心
函数是 C 语言中实现代码模块化的基础,通过封装特定功能实现代码复用与工程化管理。以下从基本概念到实战应用全面解析函数的核心知识。
一、函数的基本概念与结构
1. 核心定义:独立功能模块
函数是完成特定功能的独立代码块,具有明确的输入(参数)和输出(返回值),例如:
-
printf
:实现输出功能; -
strcpy
:实现字符串拷贝功能。
2. 四要素(函数的 “身份证”)
power
(表示求幂运算)double x, int n
(底数 x 和指数 n)void
double
(返回计算结果){}
包裹的代码,实现具体功能xⁿ
的代码3. 语法结构
返回值类型 函数名(参数列表) { // 函数体:实现功能的语句 return 结果; // 非void函数必须有return}
-
示例:求 x 的 n 次方函数
double power(double x, int n) { // 声明:返回double,接收double和int参数 double result = 1; for (int i = 0; i < n; i++) { // 函数体:循环累乘 result *= x; } return result; // 返回结果}
二、函数的声明与调用
1. 声明:告诉编译器函数 “长什么样”
函数声明(原型)用于通知编译器函数的返回值类型、名称和参数类型,格式为:
返回值类型 函数名(参数类型1, 参数类型2, ...); // 形参名可省略
-
示例:
power
函数的声明double power(double, int); // 合法:形参名可省略// 或更清晰的写法(推荐)double power(double x, int n); // 保留形参名,便于理解
-
核心作用:确保函数调用时参数类型、数量与声明一致,避免编译错误。
2. 调用:执行函数功能
函数调用需通过 “函数名 + 参数” 触发,执行流程为:
-
从
main
函数开始执行; -
遇到调用语句时,跳转到函数体执行;
-
函数执行完毕(遇到
return
或}
),返回调用处继续执行。
-
调用格式:
// 1. 无返回值函数(仅执行操作)函数名(实参); // 如:printf(\"Hello\");// 2. 有返回值函数(可接收结果或作为表达式一部分)变量 = 函数名(实参); // 如:double res = power(2, 5);
-
示例:调用
power
函数#include // 函数声明(必须在调用前)double power(double x, int n);int main() { double x = 2.5; int n = 2; // 调用函数并接收结果 double res = power(x, n); printf(\"结果:%.2lf\", res); // 输出:6.25 return 0;}// 函数实现(定义)double power(double x, int n) { double result = 1; for (int i = 0; i < n; i++) { result *= x; } return result;}
三、关键概念:形参、实参与返回值
1. 形参 vs 实参(参数传递的核心)
power
函数调用)double x, int n
(power
的形参)x=2.5, n=2
(调用时的实参)-
传递规则:实参的值会 “拷贝” 给形参,函数内部修改形参不影响实参(值传递特性)。
2. 返回值:函数的输出
-
作用:将函数计算结果传递回调用处,通过
return
语句实现。 -
规则:
-
非
void
函数必须有return
语句,且返回值类型需与函数声明一致; -
void
函数(无返回值)可省略return
,或用return;
直接结束函数。
-
-
示例:
// 非void函数:必须返回对应类型值int add(int a, int b) { return a + b; // 返回int类型,与函数声明一致}// void函数:无返回值void print_hello() { printf(\"Hello\"); // 可省略return}
四、库函数与头文件
库函数是系统提供的现成函数(如printf
、strcpy
),使用时需通过头文件获取函数原型。
1. 头文件的作用
头文件(如)包含库函数的原型声明,告诉编译器函数的参数、返回值等信息,避免 “隐式声明” 错误。
-
示例:使用
printf
必须包含:
#include // 提供printf的原型声明int main() { printf(\"Hello\"); // 编译器通过头文件确认printf的合法性 return 0;}
-
为什么需要头文件? 库函数的实现(如
printf
的具体代码)存储在系统库中,头文件仅提供 “接口说明”,确保调用时参数正确。
2. 常见库函数与对应头文件
printf
strcpy
malloc
sqrt
五、常见错误与注意事项
-
函数未声明或声明在后:
int main() { power(2, 3); // 错误:power未在调用前声明}double power(double x, int n) { ... }
解决:在
main
前添加声明double power(double, int);
。 -
形参与实参类型不匹配:
power(\"2\", 3); // 错误:实参\"2\"是字符串,形参x是double
解决:确保实参类型与形参一致(如
power(2.0, 3)
)。 -
非 void 函数缺少 return:
int add(int a, int b) { // 缺少return,编译器警告}
解决:添加
return a + b;
。 -
修改形参无法改变实参:
void change(int a) { a = 10; } // 形参是拷贝,不影响实参int main() { int x = 5; change(x); printf(\"%d\", x); // 输出5(x未被修改) return 0;}
原因:C 语言默认是 “值传递”,后续可通过指针解决此问题。
六、知识小结
对应strcpy
);忘记包含头文件的 “隐式声明” 错误power(x, n)
通过循环累乘实现;参数类型(double 接收实数)与循环逻辑(n 次乘法)七、实践建议
-
函数设计原则:
-
单一功能:一个函数只做一件事(如
power
只负责求幂); -
命名规范:见名知意(如
calculate_average
表示求平均值)。
-
-
调试技巧:
-
调用函数前打印实参,确认输入正确;
-
在函数内部打印中间结果,检查逻辑是否正确。
-
-
头文件使用:
-
自定义函数时,可将声明放在自建头文件(如
myfunc.h
),实现放在.c
文件中,模拟库函数的 “声明 - 实现分离”。
-
通过函数的模块化设计,能有效管理复杂程序,为后续学习指针函数、递归等高级特性奠定基础。
2. 函数参数传递:从值传递到地址传递的全面解析
函数参数传递是 C 语言中数据交互的核心机制,不同传递方式直接影响函数对实参的修改能力。以下从基础概念到实战应用,详解三种传递方式的原理与适用场景。
一、函数参数传递的三种方式
C 语言中函数参数传递主要有全局变量传递、复制传递(值传递)、地址传递(指针传递) 三种方式,其中后两种是主流用法。
1. 全局变量传递(不推荐)
-
核心特性: 全局变量定义在所有函数外部,所有函数均可直接访问和修改,无需通过参数传递。
int g_num = 10; // 全局变量void add() { g_num += 5; // 直接修改全局变量}int main() { add(); printf(\"%d\", g_num); // 输出15(全局变量被修改) return 0;}
-
优缺点:
-
优点:无需参数传递,实现数据共享;
-
缺点:任何函数都能修改全局变量,导致程序行为难以预测(耦合度高、调试困难)。
-
-
适用场景:几乎不推荐,仅临时测试或极简单程序使用。
2. 复制传递(值传递):形参是实参的 “副本”
-
核心原理: 函数调用时,实参的值会被拷贝到形参,形参是新开辟的独立存储空间,与实参无关。函数内部修改形参,不会影响实参。
-
示例:求 x 的 n 次方(无需修改实参)
double power(double x, int n) { // x、n是形参(实参的副本) double res = 1; for (int i = 0; i < n; i++) { res *= x; x += 1; // 修改形参x,不影响实参 } return res;}int main() { double a = 2.0; int b = 3; double result = power(a, b); // 实参a=2.0,b=3 printf(\"a=%lf, result=%lf\", a, result); // 输出:a=2.000000, result=24.000000 return 0;}
-
分析:形参
x
在函数内被修改为 3、4,但实参a
仍为 2.0(形参独立于实参)。
-
-
适用场景: 适用于无需修改实参的场景(如计算、查询),是最常用的传递方式。
3. 地址传递(指针传递):通过指针修改实参
当需要在函数内部修改实参时,必须使用地址传递 —— 实参传递变量地址,形参通过指针接收并间接访问实参。
-
核心原理: 实参是
&变量
(地址),形参是同类型指针(如int *x
)。函数内部通过*x
(解引用)直接操作实参的内存空间,实现对实参的修改。 -
示例:交换两个变量的值(必须修改实参)
// 函数:通过指针交换实参的值void swap(int *x, int *y) { // x、y是指针,接收实参地址 int temp = *x; // *x访问实参a的内存 *x = *y; // 修改实参a的值 *y = temp; // 修改实参b的值}int main() { int a = 10, b = 20; swap(&a, &b); // 传递a、b的地址 printf(\"a=%d, b=%d\", a, b); // 输出:a=20, b=10(实参被修改) return 0;}
-
关键语法:
-
形参声明:
int *x
(指针类型,用于接收地址); -
实参传递:
&a
(取变量地址); -
修改操作:
*x = ...
(解引用指针,直接操作实参内存)。
-
-
适用场景:
-
需要修改实参(如交换、排序);
-
传递大型数据(如数组、结构体),避免值传递的拷贝开销。
-
二、三种传递方式的对比
power
、add
)&a
)int *x
)swap
、数组处理)三、const 修饰符:保护数据不被修改
在地址传递中,可用const
修饰指针参数,防止函数意外修改实参,增强代码安全性。
1. 常见用法:
-
const int *x
:指针x
可指向不同地址,但不能通过*x
修改目标值(保护实参); -
int *const x
:指针x
的指向不可改,但可通过*x
修改目标值(固定指向); -
const int *const x
:指针指向和目标值均不可改(完全只读)。 -
示例:只读访问字符串(不修改原数据)
// 函数:统计字符串长度(无需修改原字符串)int str_len(const char *s) { // const保护原字符串不被修改 int len = 0; while (*s != \'\\0\') { len++; s++; // 指针可移动(非const指针) // *s = \'A\'; // 错误:const禁止修改目标值 } return len;}
四、常见错误与注意事项
-
混淆值传递与地址传递的效果: 用值传递实现交换函数(错误):
void swap(int x, int y) { // 错误:值传递,形参独立 int temp = x; x = y; y = temp; // 仅修改形参,实参不变}
解决:改用地址传递(
int *x, int *y
)。 -
指针未解引用导致修改失败:
void swap(int *x, int *y) { int *temp = x; // 错误:交换指针本身,未修改实参 x = y; y = temp;}
解决:通过
*x
和*y
操作实参内存(int temp = *x; *x = *y;
)。 -
滥用全局变量: 用全局变量传递数据导致代码耦合(一个函数修改全局变量,其他函数结果不可控)。 解决:优先使用参数传递(值传递或地址传递),减少全局变量。
-
const 修饰符使用错误:
void print(const int *x) { *x = 10; // 错误:const禁止修改目标值}
解决:
const
参数仅用于只读操作,不修改目标数据。
五、知识小结
power
)*x
修改实参int *x
接收&a
;解引用*x
才能修改实参;交换函数是典型应用const char *s
);增强代码安全性const
在*
前修饰目标(*x
不可改),在*
后修饰指针(x
不可改)strlen
)的参数设计原理六、实践建议
-
优先使用值传递:对于简单计算(如求和、求幂),值传递最安全(避免意外修改实参)。
-
必要时用地址传递:需修改实参(如排序、交换)或传递大型数据时,用指针传递。
-
善用 const 保护数据:对只读参数(如字符串、配置数据)添加
const
,防止误修改。 -
禁止滥用全局变量:通过参数传递明确数据流向,降低代码耦合度。
通过理解三种传方式的内存机制,能合理选择参数传递方式,写出安全、高效的函数。
3. 数组的传参方法:从一维数组到字符串的实战解析
数组作为 C 语言中常用的数据结构,其传参方式直接影响函数对数组的操作效率和安全性。本文围绕一维数组和字符串的传参逻辑,结合实例详解核心原理与应用技巧。
一、数组传参的两种核心方式
数组传参的本质是地址传递(区别于普通变量的值传递),但根据是否需要修改原数组,可分为两种使用场景:
1. 地址传递(默认方式,推荐)
-
核心特性: 实参传递数组名(本质是首地址),形参通过指针接收,函数内部操作直接影响原数组(无需拷贝副本,效率高)。
-
形参的两种等效声明:
// 方式1:数组形式(直观,本质是指针)void func(int arr[], int n) { ... }// 方式2:指针形式(更贴合本质)void func(int *arr, int n) { ... }
-
调用方式:
int a[] = {1, 2, 3};int len = sizeof(a) / sizeof(int); // 主函数中计算真实长度func(a, len); // 传递数组名(首地址)和长度
-
关键原理: 形参
arr[]
或int *arr
本质是指针(存储数组首地址),函数内部通过arr[i]
或*(arr + i)
访问原数组元素。
2. 复制传递(不推荐,仅特殊场景使用)
-
核心特性: 手动创建原数组的副本,函数操作副本不影响原数组(需额外内存,效率低)。
-
实现方式:
// 复制数组并操作副本void func(int *src, int *dest, int n) { // 拷贝原数组到副本 for (int i = 0; i < n; i++) { dest[i] = src[i]; } // 操作副本(不影响原数组) dest[0] = 100;}// 调用:需提前创建副本数组int a[] = {1, 2, 3};int b[3]; // 副本数组func(a, b, 3); // 原数组a不变,副本b被修改
-
适用场景: 需保护原数组且数据量较小时使用(如敏感配置数据)。
二、一维数组传参的核心问题:元素个数的传递
普通数组(如int
数组)没有终止标志,传参时必须额外传递元素个数,否则无法确定遍历范围。
1. 典型错误:在函数内用sizeof
计算长度
// 错误示例:函数内用sizeof计算数组长度int sum(int arr[]) { int len = sizeof(arr) / sizeof(int); // 错误!arr是指针,sizeof(arr)=4 int s = 0; for (int i = 0; i < len; i++) { // len=1,仅遍历第一个元素 s += arr[i]; } return s;}
-
错误原因: 形参
arr[]
本质是指针(int *arr
),sizeof(arr)
在 32 位系统中为 4 字节,除以int
的 4 字节得到len=1
,导致遍历不完整。
2. 正确做法:主函数计算长度并传递
// 正确示例:额外传递元素个数int sum(int arr[], int len) { // 接收长度参数 int s = 0; for (int i = 0; i < len; i++) { s += arr[i]; } return s;}// 调用int main() { int a[] = {1, 2, 3, 4}; int len = sizeof(a) / sizeof(int); // 主函数计算真实长度 printf(\"和为:%d\", sum(a, len)); // 输出10 return 0;}
三、字符串(字符数组)的传参特殊性
字符串以\'\\0\'
为终止标志,传参时无需额外传递长度,可通过\'\\0\'
判断结束。
1. 案例:删除字符串中的空格(双指针法)
// 函数:删除字符串中所有空格,直接修改原字符串void del_space(char *str) { char *s1 = str; // 快指针:遍历所有字符 char *s2 = str; // 慢指针:记录非空格字符 while (*s1 != \'\\0\') { // 以\'\\0\'为终止条件,无需额外传长度 if (*s1 != \' \') { // 非空格字符:赋值给慢指针位置 *s2 = *s1; s2++; // 慢指针后移 } s1++; // 快指针始终后移 } *s2 = \'\\0\'; // 手动添加终止符(覆盖剩余字符)}// 调用int main() { char s[] = \"a b c d\"; // 字符数组(可修改) del_space(s); printf(\"%s\", s); // 输出\"abcd\" return 0;}
2. 关键技巧:双指针法
-
快指针(
s1
):负责遍历原字符串,跳过空格; -
慢指针(
s2
):负责记录非空格字符,实现 “原地修改”; -
最后补
\'\\0\'
:确保字符串正确终止(覆盖原字符串中剩余的空格或字符)。
四、数组与字符串传参的对比
int
)int arr[]
或 int *arr
char
数组)\'\\0\'
char str[]
或 char *str
五、常见错误与注意事项
-
字符串常量无法修改:
char *s = \"a b c\"; // 指向字符串常量(只读)del_space(s); // 错误!尝试修改常量区数据
解决:用字符数组
char s[] = \"a b c\";
(可修改)。 -
忘记传递普通数组的长度:
sum(a); // 错误!未传递长度,函数无法遍历
解决:始终传递
sum(a, len)
,len
在主函数中计算。 -
双指针法遗漏
\'\\0\'
: 删除空格后未补*s2 = \'\\0\'
,导致输出乱码。 解决:循环结束后必须添加终止符。
六、知识小结
int arr[]
本质是int *arr
sizeof
在形参中失效(返回指针大小);必须在主函数计算长度并传递\'\\0\'
判断结束;形参char str[]
本质是char *str
char *s
)不可修改;字符数组(char s[]
)可修改\'\\0\'
七、实践建议
-
普通数组传参步骤:
-
主函数计算长度:
len = sizeof(a) / sizeof(a[0])
; -
传递数组名和长度:
func(a, len)
; -
函数内用
len
控制遍历。
-
-
字符串处理技巧:
-
用字符数组存储可修改的字符串;
-
双指针法实现原地修改(高效,无额外内存);
-
始终检查
\'\\0\'
作为终止条件。
-
-
调试方法:
-
打印数组长度和指针地址,验证遍历范围;
-
字符串处理后打印
*s2
位置,确认\'\\0\'
已添加。
-
掌握数组与字符串的传参逻辑,能高效处理批量数据,为后续二维数组、结构体传参奠定基础。
4. 指针函数 1 :内存管理的核心战场
指针函数作为 C 语言中最具威力的特性之一,其核心难点在于返回指针的生命周期管理。理解不同存储类型的特性,是避免 \"野指针\" 和内存泄漏的关键。
一、指针函数的本质与致命陷阱
指针函数是返回值为地址(指针)的函数,但并非所有地址都能安全返回。最常见的错误是返回局部变量的地址:
// 错误示例:返回局部数组的地址char* get_string() { char str[10]; // 局部数组(栈内存) strcpy(str, \"hello\"); return str; // 错误!函数返回后str内存被回收}int main() { char* p = get_string(); // p指向已释放的内存 printf(\"%s\", p); // 可能输出乱码或崩溃 return 0;}
-
错误本质: 局部变量存储在栈内存,函数返回时栈帧被销毁,内存被系统回收。此时返回的指针成为 \"野指针\",访问它会导致未定义行为(如乱码、崩溃)。
二、四种合法的返回值类型
为确保返回的指针有效,必须指向以下四种内存区域:
1. 全局变量地址
char global_str[20]; // 全局变量(静态区)char* get_string() { strcpy(global_str, \"hello\"); return global_str; // 安全:全局变量生命周期为整个程序}
-
优点:简单直接;
-
缺点:破坏封装性(全局可见),易引发命名冲突。
2. 静态变量地址(推荐)
char* get_string() { static char str[20]; // 静态变量(静态区) strcpy(str, \"hello\"); return str; // 安全:静态变量生命周期为整个程序}
-
优点:变量仅在函数内可见,保持封装性;
-
缺点:多次调用会覆盖上次结果(线程不安全)。
3. 字符串常量地址(只读场景)
char* get_string() { return \"hello\"; // 安全:字符串常量存储在只读区}int main() { char* p = get_string(); // *p = \'H\'; // 错误!尝试修改只读内存 return 0;}
-
适用场景:返回固定字符串(如配置信息);
-
限制:不可修改字符串内容(否则段错误)。
4. 动态分配的内存(灵活但需谨慎)
char* get_string() { char* str = malloc(20); // 堆内存 if (str == NULL) exit(1); // 检查分配失败 strcpy(str, \"hello\"); return str; // 安全:堆内存需手动释放}int main() { char* p = get_string(); printf(\"%s\", p); free(p); // 必须释放!否则内存泄漏 return 0;}
-
优点:每次调用返回独立内存,可修改内容;
-
风险:必须由调用者负责
free()
,否则导致内存泄漏。
三、内存管理的黄金法则
四、实战案例:字符串处理函数设计
1. 错误案例:返回局部数组
// 错误:返回局部数组地址char* remove_spaces(char* input) { char result[100]; // 局部数组 int j = 0; for (int i = 0; input[i] != \'\\0\'; i++) { if (input[i] != \' \') { result[j++] = input[i]; } } result[j] = \'\\0\'; return result; // 错误!返回局部变量地址}
2. 正确方案:静态变量
// 方案1:静态变量(适合不需要线程安全的场景)char* remove_spaces(char* input) { static char result[100]; // 静态数组 int j = 0; for (int i = 0; input[i] != \'\\0\'; i++) { if (input[i] != \' \') { result[j++] = input[i]; } } result[j] = \'\\0\'; return result; // 安全:静态变量生命周期为整个程序}
3. 正确方案:动态内存
// 方案2:动态内存(适合需要独立副本的场景)char* remove_spaces(char* input) { char* result = malloc(strlen(input) + 1); // 分配足够内存 if (result == NULL) exit(1); int j = 0; for (int i = 0; input[i] != \'\\0\'; i++) { if (input[i] != \' \') { result[j++] = input[i]; } } result[j] = \'\\0\'; return result; // 安全:需调用者free()}// 调用者必须释放内存int main() { char* s = remove_spaces(\"hello world\"); printf(\"%s\\n\", s); free(s); // 关键!避免内存泄漏 return 0;}
五、常见错误与防御性编程
-
混淆静态变量与局部变量:
char* func() { char str[10]; // 局部变量(错误) static char s[10]; // 静态变量(正确) return str; // 致命错误!}
防御:检查返回的指针是否指向栈内存(局部变量)。
-
忘记释放动态内存:
void leak_memory() { char* p = malloc(100); // 使用p... // 忘记free(p)!} // 内存泄漏:p指向的内存无法再被访问
防御:遵循 \"谁分配,谁释放\" 原则,或在文档中明确告知调用者。
-
修改字符串常量:
char* s = \"hello\";s[0] = \'H\'; // 段错误!尝试修改只读内存
防御:使用字符数组存储可修改的字符串:
char s[] = \"hello\";
。
六、知识小结
数据类型 *函数名()
数据类型 (*指针名)()
);返回值必须指向有效内存static
延长变量生命周期,保持封装性malloc
分配内存,调用者负责free
free
导致内存泄漏;malloc
后需检查NULL
(防止空指针)\"hello\"
),但不可修改内容char* s = \"hi\"
(只读)和char s[] = \"hi\"
(可写)七、实践建议
-
优先使用静态变量: 若函数无需线程安全(单线程环境),优先用
static
变量返回结果(简单高效)。 -
动态内存的使用场景: 当需要多次调用且每次结果独立时(如多线程),使用
malloc
分配内存,并确保:-
调用者文档中明确标注
free()
责任; -
分配后立即检查
NULL
(防止空指针)。
-
-
字符串常量的安全使用: 仅在返回固定不变的字符串时使用(如配置信息),禁止修改内容。
-
调试技巧:
-
用
valgrind
检测内存泄漏; -
对返回的指针添加
assert(p != NULL)
检查; -
避免复杂函数嵌套返回指针(保持逻辑清晰)。
-
通过严格控制指针的生命周期,指针函数能成为高效编程的利器,否则将成为程序崩溃的导火索。