函数调用基本规则调用前必须先定义 / 声明函数调用运算符()不可省略即使无参数参数传递要求个数一致、类型匹配类型一致或可隐式转换函数调用过程函数调用遵循栈式调用规则先调用的后返回voidfun3(){printf(this is fun3...\n);return;}voidfun2(){fun3();printf(this is fun2...\n);return;}voidfun1(){fun2();printf(this is fun1...\n);return;}intmain(){fun1();printf(this is main...\n);return0;}调用流程main() → fun1() → fun2() → fun3()返回流程fun3() return → fun2() return → fun1() return → main() return函数参数传递1. 值传递定义实参将数据内容复制一份给形参形参是实参的独立副本特点只能读取实参的值无法通过修改形参改变实参适用场景仅需要获取实参数据不需要修改voidfun(intarg){arg;printf(fun arg:%d\n,arg);}voidswap(inta,intb){intta;ab;bt;printf(swap a:%d b:%d\n,a,b);}intmain(){inti10;fun(i);printf(i:%d\n,i);// i仍为10未被修改inta20,b1;swap(a,b);printf(main a:%d,b:%d\n,a,b);// a20,b1交换失败return0;}2. 地址传递定义实参传递变量的内存地址给形参特点形参通过间接访问解引用可以读写实参的数据适用场景需要修改实参的值、需要返回多个结果一维整形数组传参本质地址传递数组名会降级为指针64 位系统占 8 字节注意被调函数内无法通过sizeof(a)/sizeof(a[0])计算数组长度必须显式传入 sizevoidfill_array(inta[],intsize){// 错误sizeof(a) 8指针大小不是数组总大小// int size sizeof(a)/sizeof(a[0]);for(inti0;isize;i){a[i]rand()%30;// 可以直接修改原数组}}intmain(){srand(time(NULL));intarray[10]{0};intsizesizeof(array)/sizeof(array[0]);fill_array(array,size);// 传入数组名首地址return0;}一维字符数组传参本质地址传递特点字符串以\0结尾无需传入长度可通过判断\0获取有效长度intmystrlen(chara[]){intlen0;while(a[len]!\0){len;}returnlen;}intmain(){charstr[100]{0};gets(str);intlenmystrlen(str);printf(字符串长度%d\n,len);return0;}常用函数版本实现功能函数原型数组填充void fill_array(int a[], int size)数组逆序void array_reverse(int a[], int size)冒泡排序void bubble_sort(int a[], int size)选择排序void select_sort(int a[], int size)字符串长度int mystrlen(char s[])字符串复制char* mystrcpy(char dest[], const char src[])字符串拼接char* mystrcat(char dest[], const char src[])字符串比较int mystrcmp(const char s1[], const char s2[])函数递归定义在函数内部自己调用自己的函数调用形式属于循环的一种表现形式。核心规则1. 必须设置**合理的退出条件**否则会无限递归导致栈溢出 2. 递归层次不宜过深会消耗大量栈内存 3. 适用场景回溯类、逆向思维问题阶乘、汉诺塔1. 阶乘计算int factorial(int n) { // 退出条件 if(n 1 || n 0) { return 1; } // 递归调用 return factorial(n-1) * n; } int main() { int ret factorial(3); printf(3的阶乘%d\n, ret); // 输出6 return 0; }汉诺塔问题voidhan(intn,chara,charb,charc){if(n1){printf(%c-%c\n,a,c);}else{han(n-1,a,c,b);printf(%c-%c\n,a,c);han(n-1,b,a,c);}}intmain(){han(5,a,b,c);// 5个盘子从a移到c借助breturn0;}预处理命令所有以#开头的指令在编译预处理阶段执行不参与编译。#include 头文件包含本质将被包含文件的内容完整复制到当前位置两种写法#include stdio.h在系统头文件目录/usr/include查找#include myfile.h先在当前目录查找再去系统目录查找注意禁止包含.c 文件#define 宏定义本质纯文本替换不进行任何数据计算和类型检查规则宏值如果是表达式必须用圆括号包裹避免优先级错误1. 不带参宏#definePI3.14#defineM50#defineN((M)(M))// 必须加括号否则 N*N 会变成 5050*50502. 带参宏#defineMAX(x,y)((x)(y)?(x):(y))#defineADD(a,b)((a)(b))// 多行宏#definePRINT(a,b,c)do{\printf(a is %d\n,a);\printf(b is %d\n,b);\printf(c is %d\n,c);\}while(0)带参宏 vs 函数特性带参宏函数参数类型无类型纯替换有严格类型检查执行阶段预处理阶段运行阶段代码体积多次调用会膨胀代码只有一份性能无调用开销有参数传递和跳转开销适用场景功能简单的单行逻辑复杂功能、多行代码条件编译根据条件决定是否编译某段代码用于宏开关、版本控制。#defineDEBUG1#ifDEBUG// 调试代码DEBUG为1时编译printf(调试信息\n);#else// 正式版代码#endif// 头文件保护防止头文件重复包含#ifndef__ADD_H__#define__ADD_H__// 头文件内容intadd(inta,intb);#endif指针核心概念内存地址每个 1 字节的内存单元的唯一编号线性连续指针专门存储内存地址的变量指针大小32 位系统 4 字节64 位系统所有类型指针都是 8 字节指针类型决定了解引用时读取的字节数和指针算术运算的偏移量相关运算符运算符功能说明取地址运算符获得变量的内存地址单目右结合只能作用于变量左值不能作用于常量 / 表达式*解引用运算符通过地址访问对应内存的数据单目右结合只能作用于指针变量互为反操作*a a指针定义与初始化// 定义语法存储类型 数据类型 * 变量名;int*pa;// 整形指针存储int变量的地址char*pcNULL;// 空指针指向0地址double*pd;// 野指针未初始化存储随机地址禁止读写// 正确初始化inta20;paa;// pa指向变量a的地址指针的算术运算指针仅支持、-、、--运算且仅对数组有意义。偏移规则p n 原地址 n * sizeof(基类型)指针相减两个指向同一数组的指针相减结果为相差的元素个数intarr[8]{0};int*p_startarr[0];int*p_endarr[7];intdiffp_end-p_start;// diff 7相差7个元素大小端存储多字节数据在内存中的字节顺序由 CPU 决定小端存储Intel/AMD/ARM数据低位存低地址高位存高地址大端存储51 单片机、网络设备数据低位存高地址高位存低地址// 判断大小端intmain(){inta0x12345678;char*p(char*)a;if(*p0x78){printf(小端存储\n);}else{printf(大端存储\n);}return0;}指针的左右值区分表达式左值含义右值含义p修改指针本身存储的地址取出指针存储的地址*p向指针指向的变量写入数据读取指针指向变量的数据inta10,b20;int*pa;pb;// p做左值修改指针指向int*qp;// p做右值复制指针地址*p30;// *p做左值修改b的值为30intc*p;// *p做右值读取b的值指针作为函数参数作用 1让被调函数修改主调函数的变量作用 2函数需要返回多个结果时通过指针参数带出// 正确的交换函数地址传递voidswap(int*pa,int*pb){intt*pa;*pa*pb;*pbt;}intmain(){inta1,b2;swap(a,b);// 传入变量地址printf(a%d, b%d\n,a,b);// a2, b1交换成功return0;}指针与数组的关系数组名是指向数组首元素的指针常量本身的值不能修改大部分场景下int a[]和int* a可以互换核心区别sizeof(a)数组名返回数组总大小指针返回 8 字节a数组名返回数组指针int (*)[n]指针返回二级指针int**// 数组元素的两种访问方式intarr[5]{1,2,3,4,5};// 下标访问printf(%d\n,arr[2]);// 指针访问等价于下标printf(%d\n,*(arr2));