Days 10 函数与预处理
前言最近两天学完 C 语言函数和预处理命令两大模块踩了好多坑值传递改不了外部变量、数组传参本质传地址、宏只是纯文本替换、递归忘写终止条件直接栈溢出…… 把课堂知识点 课后 6 道字符串作业全部整理出来适合刚入门的新手复习代码全部可直接运行。一、2.5 函数核心知识点1. 函数基础三件套定义、调用、声明函数定义写清函数返回值、函数名、形参列表、函数体实现具体功能函数调用主函数 / 其他函数中直接写函数名 实参执行内部代码函数声明如果函数写在 main 函数后面必须在 main 前声明告诉编译器函数格式2. 三种函数传参方式重中之重新手最容易混淆1赋值传递值传递 / 复制传递规则实参把值拷贝一份给形参形参只是副本修改形参完全不影响外部实参。 适用场景只读取外部变量值不需要修改外部变量 禁止场景想要修改外部变量值传递做不到。c运行#include stdio.h void change(int num) { num 999; // 只改副本外面变量不变 } int main() { int a 10; change(a); printf(a %d, a); // 输出10不会变999 return 0; }2全局变量传递数据全局变量定义在所有函数外面整个文件所有函数都能访问、修改局部变量和全局变量重名时局部优先会隐藏全局变量缺点不安全、耦合高项目里尽量少用全局变量。3数组传递地址传递数组传参只传数组首地址函数内操作数组和外部是同一块内存修改会同步生效一维整型数组需要传数组名 数组长度c运行int arr[5]; void bubbleSort(int array[], int len); // 冒泡排序封装一维字符数组字符串可以传数组名依靠 \0 自动判断结束可不用传长度c运行char str[32] hello; int MyStrcpy(char pstr[]);3. 递归函数定义函数内部调用自己就是递归两个硬性要求缺一不可必须写递归结束条件否则无限递归栈溢出程序崩溃控制递归深度太深效率极低课堂练习递归求斐波那契第 n 项数列1 1 2 3 5 8…c运行#include stdio.h // 递归获取斐波那契第n项 int fib(int n) { // 终止条件 if(n 1 || n 2) { return 1; } // 递归公式 return fib(n-1) fib(n-2); } int main() { printf(%d, fib(10)); // 输出55 return 0; }二、2.6 预处理命令# 开头指令编译前提前处理1. 宏定义 #define宏名约定全大写和普通变量区分核心本质纯文本替换不会自动计算。1不带参宏常量宏用来定义固定常量修改只改一处提升可读性c运行#define MAX_LEN 32 #define PI 3.1415坑点宏原样替换表达式不加括号会运算出错c运行#define N 55 int a N * N; // 替换后55*55 35不是10*10100 // 正确写法#define N (55)2带参宏宏函数和普通函数区别 带参宏文本替换无参数类型 / 返回值无调用开销执行快多次使用会增大代码体积 普通函数有参数、返回值类型严格存在传参调用开销不会增大程序体积 适用场景短小简单逻辑复杂逻辑优先用函数。 示例取两个数最大值c运行#define MAX(x,y) ((x)(y)?(x):(y))2. 条件编译只满足条件的代码才参与编译常用于调试、多版本切换c运行#if 条件 代码1 #elif 条件 代码2 #else 代码3 #endif // 如果没定义宏才编译代码 #ifndef DEBUG // 代码 #endif // 如果定义宏才编译代码 #ifdef DEBUG // 调试打印代码 #endif3. 头文件包含 #include作用把头文件内容复制展开到当前.c 文件 两种写法区别#include stdio.h 尖括号去系统头文件目录 /usr/include 查找#include myhead.h 双引号先在当前代码文件夹找找不到再去系统目录。头文件一般存放自定义数据类型、全局函数声明、宏定义。三、课后 6 道作业完整代码封装自定义字符串函数作业 1MyStrlen 手动统计字符串长度不含 \0c运行#include stdio.h // 自定义求字符串长度 int MyStrlen(char str[]) { int len 0; while(str[len] ! \0) { len; } return len; } int main() { char buf[100]; printf(请输入字符串); scanf(%s, buf); printf(字符串长度%d, MyStrlen(buf)); return 0; }作业 2MyStrcpy 字符串拷贝c运行#include stdio.h void MyStrcpy(char dest[], char src[]) { int i 0; while(src[i] ! \0) { dest[i] src[i]; i; } dest[i] \0; // 手动补结束符 } int main() { char str1[100]; char str2[100]; printf(输入源字符串); scanf(%s, str2); MyStrcpy(str1, str2); printf(拷贝结果%s, str1); return 0; }作业 3MyStrcat 字符串拼接第二个拼到第一个末尾c运行#include stdio.h void MyStrcat(char dest[], char src[]) { int i 0, j 0; // 先走到dest末尾 while(dest[i] ! \0) { i; } // 拼接src while(src[j] ! \0) { dest[i] src[j]; } dest[i] \0; } int main() { char s1[100] hello; char s2[50]; printf(输入要拼接的字符串); scanf(%s, s2); MyStrcat(s1, s2); printf(拼接后%s, s1); return 0; }作业 4MyStrcmp 字符串比较按 ASCII 码规则逐字符对比相等返回 0str1 大返回正数str1 小返回负数c运行#include stdio.h int MyStrcmp(char s1[], char s2[]) { int i 0; while(s1[i] ! \0 s2[i] ! \0) { if(s1[i] ! s2[i]) { return s1[i] - s2[i]; } i; } // 长度不一样比较末尾差值 return s1[i] - s2[i]; } int main() { char a[50], b[50]; printf(输入字符串1); scanf(%s, a); printf(输入字符串2); scanf(%s, b); int ret MyStrcmp(a,b); if(ret 0) printf(s1 s2); else if(ret 0) printf(s1 s2); else printf(两个字符串相等); return 0; }作业 5字符串倒置函数c运行#include stdio.h int MyStrlen(char str[]) { int len 0; while(str[len]) len; return len; } void ReverseStr(char str[]) { int len MyStrlen(str); int left 0, right len - 1; char temp; while(left right) { temp str[left]; str[left] str[right]; str[right] temp; left; right--; } } int main() { char buf[100]; printf(输入字符串); scanf(%s, buf); ReverseStr(buf); printf(反转后%s, buf); return 0; }四、新手易错点总结避坑指南值传递永远改不了外部变量数组 / 指针传参才能修改外部数据递归不写终止条件直接程序崩溃递归求斐波那契效率很低宏只是文本替换表达式一定要套括号否则运算优先级出错字符串操作必须手动处理 \0忘记补结束符会乱码头文件双引号和尖括号查找路径不同自定义头文件用双引号全局变量尽量少用局部变量和全局重名时局部优先数组传参只会传递首地址函数内修改数组会影响外部原数组。结尾函数是 C 语言代码模块化的核心预处理是编译前的基础操作手动模拟库字符串函数能彻底理解字符串底层逻辑。笔记持续更新有写错的地方欢迎大佬评论指正

相关新闻