是不是刚学 C 语言就被 “函数” 和 “指针” 拦住了?定义函数时参数总写错,调用的时候要么传错值,要么忘写括号;一到指针更是头大,“*” 和 “&” 像绕口令,好不容易写两行代码,编译就报 “segmentation fault”,根本不知道错在哪。其实啊,函数和指针是 2024 年学 C 语言绕不开的核心,哪怕是零基础,找对方法也能快速掌握。今天兔子哥就用 2024 最新的思路,从函数入门到指针用法,再到实战案例,一步步带新手吃透这两个难点,保证看完就知道该怎么练!
一、先搞懂函数:重复代码太多?用函数打包就对了
现象:写代码时总在复制粘贴,改一处要改十处
小李最近写了个计算圆面积的程序,需要在三个地方用到面积计算,他就把计算公式复制了三遍。后来老师说 “半径单位要改”,他改了两处漏了一处,结果程序出了错。这就是没用到函数的麻烦 —— 重复代码多,维护起来费劲。
1. 函数就是 “代码打包机”,用一次定义多次用
函数其实就是把一段常用代码包起来,起个名字,想用的时候喊名字就行。比如计算圆面积,定义一个
circle_area函数,哪里需要就调用哪里:c运行
#include // 定义函数:计算圆面积,参数是半径rfloat circle_area(float r) {float area = 3.14 * r * r;return area; // 返回计算结果}int main() {// 调用函数,传不同的半径printf("半径2的面积:%f\n", circle_area(2));printf("半径3的面积:%f\n", circle_area(3));return 0;}这样改公式时,只需要改
circle_area里的代码,不用到处找复制的地方,方便多了。2. 函数参数:别搞混 “形参” 和 “实参”
很多新手在参数上出错,其实简单说:
- 定义函数时写的参数(比如
circle_area(float r)里的r)叫 “形参”,就像个占位符; - 调用函数时传的值(比如
circle_area(2)里的2)叫 “实参”,是真正要用的数据。
形参和实参的类型要一样,比如形参是float,实参也得是小数或float变量,不然编译会报错。
3. 虽然函数看起来简单,但传参方式有坑
函数传参分 “值传递” 和 “地址传递”,新手容易搞混:
- 值传递:实参把值传给形参,形参改了不影响实参。比如传
2给r,在函数里改r,外面的2不变; - 地址传递:传变量的地址(用
&),形参改了,实参也会变。这得结合指针用,后面会讲。
不过话说回来,刚开始不用急着分清,先练熟值传递,后面学指针时再回头看,就容易懂了。
二、再学指针:别被 “地址” 吓到,其实就是 “门牌号”
现象:看到int *p就懵,不知道p和*p啥区别
小王对着教程学指针,写了句
int *p = 10,编译报错 “类型不匹配”,他盯着屏幕半小时,也没明白为啥不能直接给指针赋值。其实指针存的是地址,不是普通数值,就像门牌号上不能写 “苹果”,得写具体的号码。1. 指针的本质:存地址的变量
咱们用个生活例子:你家有个盒子(变量
a),里面放着糖果(值5),盒子上的门牌号(0x123)就是地址。指针p就是一张纸条,上面写着这个门牌号。c运行
#include int main() {int a = 5; // 变量a,值5int *p; // 定义int型指针pp = &a; // &a取a的地址,p存这个地址(纸条写上0x123)printf("a的值:%d\n", a); // 盒子里的糖果:5printf("p存的地址:%p\n", p); // 纸条上的门牌号:0x123printf("p指向的值:%d\n", *p); // *p按地址找盒子,拿糖果:5return 0;}记住:
p是地址,*p是地址里的值,&a是a的地址,这三个符号别弄混。2. 指针和函数结合:解决 “值传递改不了实参” 的问题
前面说值传递改不了实参,比如想写个交换两个数的函数,用值传递就不行:
c运行
// 错误示例:值传递,交换不了void swap(int x, int y) {int temp = x;x = y;y = temp;}int main() {int a = 3, b = 5;swap(a, b);printf("交换后:%d, %d", a, b); // 还是3,5,没交换return 0;}这时候就得用指针传地址,让函数能直接改实参:
c运行
// 正确示例:传地址,能交换void swap(int *x, int *y) {int temp = *x; // 取x指向的值*x = *y; // 把y指向的值给x指向的变量*y = temp;}int main() {int a = 3, b = 5;swap(&a, &b); // 传a和b的地址printf("交换后:%d, %d", a, b); // 输出5,3,成功交换return 0;}三、练习案例:用函数和指针做个 “成绩处理工具”
光说不练假把式,这个案例能串起所有知识点
功能:输入 3 个学生成绩,用函数求平均分,用指针找最高分。
完整代码(带注释):
c运行
#include // 函数:计算平均分float get_average(int scores[], int n) {int sum = 0;for (int i = 0; i < n; i++) {sum += scores[i];}return sum / (float)n; // 转成float,避免整数除法}// 函数:用指针找最高分void find_max(int scores[], int n, int *max) {*max = scores[0]; // 先假设第一个是最高分for (int i = 1; i < n; i++) {if (scores[i] > *max) {*max = scores[i];}}}int main() {int scores[3], max;// 输入成绩printf("输入3个成绩:");for (int i = 0; i < 3; i++) {scanf("%d", &scores[i]);}// 调用函数求平均分float avg = get_average(scores, 3);// 调用函数找最高分(传max的地址)find_max(scores, 3, &max);printf("平均分:%f,最高分:%d", avg, max);return 0;}新手练习:
- 改改代码,让它能计算平均分并判断是否及格(60 分以上);
- 试试用指针遍历数组,替换掉
for循环里的下标scores[i]。
四、常见错误对照表:这些坑你大概率会踩
| 错误类型 | 代码示例 | 原因 | 解决 |
|---|---|---|---|
| 函数调用漏括号 | printf("hello")写成printf "hello" | 函数调用必须加() | 加上(),printf("hello") |
| 指针未初始化 | int *p; *p = 5; | 指针没指向有效地址,是野指针 | 先让p指向变量,int a; p=&a *p=5; |
| 形实参类型不匹配 | 函数void f(float x),调用f(3) | 实参是 int,形参是 float | 传3.0或强制转换f((float)3) |
五、自问自答:新手最常问的 2 个问题
Q:“函数和指针哪个更难?先学哪个?”
A:建议先学函数,函数是基础,用熟了再学指针。指针确实绕,但结合具体例子(比如交换两个数),慢慢就懂了。学会函数或许能帮助写出更高效的代码,但指针能让代码更灵活,两者都得掌握。
Q:“指针为什么会报‘segmentation fault’?”
A:大多是访问了不该访问的地址,比如指针没初始化就用,或者越界访问数组。关于指针在不同编译器中的细微差异,具体机制待进一步研究,但新手只要保证指针指向有效变量,别越界,基本能避免这个错。
兔子哥觉得,学函数和指针,关键是多动手敲代码,别光看教程。函数要练到 “看到重复代码就想打包”,指针要多画图理解地址和值的关系,比如画个盒子代表变量,旁边标上门牌号(地址),指针就是指向门牌号的箭头。
我刚开始学指针时,写交换函数错了十几次,每次都盯着
*和&发呆,后来把代码打印出来,在纸上画箭头标地址,突然就明白了。新手别怕错,错一次就记一次原因,慢慢就摸透规律了。现在就打开编译器,从写一个简单的加法函数开始,再试试用指针改它的值,你会发现函数和指针没那么难,动手试试吧!标签: segmentation 打包机
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~