关注

什么?C语言函数学不明白?那让“众包大神”来教你

目录

一、函数是啥?就是代码界的 “外卖小哥”

 1. 补充一些知识:

二、函数的 “三要素”:返回类型 + 函数名 + 参数列表

三、形参和实参:别搞混的 “双胞胎”

四、函数的 “潜规则”:这些坑千万别踩

1. 返回值会 “自动变形”?小心被坑!

2. 分支语句必须 “雨露均沾”,别厚此薄彼

3. 数组传参:表面兄弟,实则 “地址传递”

4.return 语句:函数的 “下班信号”

五、函数的 “高级玩法”:嵌套调用和链式调用

1. 嵌套调用:函数里再喊其他函数,分工明确

2. 链式调用:函数的返回值直接当另一个函数的参数

六、函数声明和定义:别让编译器 “迷路”

七、多文件写法:企业级规范

八、总结:函数是代码的 “万能工具”


  记得刚写代码时总爱把所有逻辑堆在main函数里,就像把做饭、洗碗、拖地全塞给一个人干 —— 代码乱成 “一锅粥” 不说,改个功能得从头翻到尾。这时候函数就得登场了,它给我的感觉就像送外卖的“众包大神”,专门接手各种不同的单子和任务。

  

一、函数是啥?就是代码界的 “外卖小哥”

  简单说,函数就是 “专门干一件事的代码块”。就像外卖小哥:你下单(调用函数),他专门负责把餐送到你手上(执行特定功能),完事就走人(返回结果)。

看个最简单的例子:

// 定义一个专门做加法的函数(就像专门送奶茶的小哥)
int Add(int x, int y) {
    return x + y; // 接到两个单子,加起来送回去
}

int main() {
    int a = 0, b = 0;
    scanf("%d%d", &a, &b);
    int c = Add(a, b); // 调用Add函数,把a和b“下单”过去
    printf("%d\n", c);
    return 0;
}

  以前你可能直接写c = a + b,但用了函数就不一样了:以后不管在哪需要加法,直接喊Add就行,不用每次都写一遍计算过程。这就像你想喝奶茶,自己动手做可能很费劲,直接点外卖就行 —— 省时省力还不容易错!

 1. 补充一些知识:

      C语⾔标准中规定了C语⾔的各种语法规则,C语⾔并不提供库函数;C语⾔的国际标准ANSI C规定了⼀ 些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列 函数的实现。这些函数就被称为 库函数
       我们前⾯内容中学到的 printf scanf 都是库函数,库函数也是函数。是可以直接拿来使用的
各种编译器的标准库中提供了⼀系列的库函数,这些库函数根据功能的划分,都在不同的头⽂件中进⾏了声明。
库函数相关头⽂件: https://zh.cppreference.com/w/c/heade

二、函数的 “三要素”:返回类型 + 函数名 + 参数列表

  函数也有 “身份证”,三要素一个都不能少:

  1. 返回类型:函数执行完给你啥 “结果”(比如int表示返回整数,void表示啥也不返回)
  2. 函数名:给函数起个名(就像外卖平台的 “奶茶店名字”)
  3. 参数列表:你要传给函数的 “材料”(比如Add函数需要两个整数xy函数使⽤的过程中,把函数的参数分为,实参形参

  举个 “啥也不返回” 的例子:

// void表示:我只干活,不给结果(就像清洁工,干完就走)
void print_hehe() {
    printf("hehe\n"); // 专门负责打印hehe
}

int main() {
    print_hehe(); // 喊它一声,它就打印hehe
    print_hehe(); // 再喊一声,再打一次
    return 0;
}

  运行结果会打印两次hehe,感觉用上了函数让代码逻辑更加清楚有条理。是不是比写两次printf清爽多了?这就是函数的第一个好处:代码复用,少写重复劳动

三、形参和实参:别搞混的 “双胞胎”

  调用函数时,你会遇到两个概念:形参和实参,比如Add(a, b)里,ab是实参,Add函数里的xy是形参。

简单说:

  • 实参:你实际传给函数的 “原材料”,比如ab,是真实存在的变量。
  • 形参:函数用来接原材料的 “容器”,比如xy,只有函数被调用时才会临时创建,用完就销毁。

  重点:形参是实参的 “临时拷贝”,就像你把奶茶配方(实参)抄给朋友(形参),朋友改他的抄本,不会影响你的原配方。比如你在Add里改x的值,a的值不会变:

int Add(int x, int y) {
    x = x + 1; // 改x的值
    return x + y;
}

int main() {
    int a = 3, b = 5;
    int sum = Add(a, b); // 这里x变成4,a还是3
    printf("a=%d, sum=%d\n", a, sum); // 输出a=3, sum=9
    return 0;
}

四、函数的 “潜规则”:这些坑千万别踩

  函数虽然好用,但规矩不少,踩坑了能让你怀疑人生。咱们一个个说:

1. 返回值会 “自动变形”?小心被坑!

函数的返回值会自动转换成 “返回类型”,但这可能帮倒忙:

int test() {
    return 3.14; // 函数说返回int,你给个小数?
}

int main() {
    int r = test();
    printf("%d\n", r); // 猜猜输出啥?
    return 0;
}

  答案是3。因为int类型只能存整数,小数部分会被直接砍掉(不是四舍五入)。

2. 分支语句必须 “雨露均沾”,别厚此薄彼

如果函数里有if分支,一定要保证每种情况都有返回值,不然编译器会给你 “使绊子”

int is_odd(int n) {
    if (n % 2 == 1) {
        return 1; // 奇数返回1
    }
    // 这里漏了else!偶数咋办?
}

  这种情况下,要是传入偶数,函数就不知道该返回啥了,可能会返回一个随机数(就像外卖小哥说不好意思你的筷子丢了一样*-*)。正确写法应该是:

int is_odd(int n) {
    if (n % 2 == 1) {
        return 1;
    } else {
        return 0; // 偶数也有返回值,完美!
    }
}

   

3. 数组传参:表面兄弟,实则 “地址传递”

数组传参时,函数里的数组名其实是个 “骗子”—— 它看起来是数组,实际是数组首元素的地址。不信看代码:

void set_arr(int arr[10], int sz) { // 这里的[10]只是个摆设,没用!
    for (int i = 0; i < sz; i++) {
        arr[i] = -1; // 能修改原数组,因为传的是地址
    }
}

int main() {
    int arr[10] = {0};
    int sz = sizeof(arr) / sizeof(arr[0]); // 必须在外面算数组长度!
    set_arr(arr, sz); // 传数组名和长度
    return 0;
}

  为啥要单独传sz?在上一讲中有详细说到。因为在函数里用sizeof(arr)算出来的不是数组长度(而是地址的大小),这就像你给朋友寄一箱苹果,他只收到了快递单(地址),不知道箱子里有几个苹果,所以你得提前告诉他数量,避免被别人偷吃了。

4.return 语句:函数的 “下班信号”

 return语句就像函数的 “下班铃”,一旦执行,函数就立马结束,后面的代码再写也没用。比如:

int Add(int x, int y) {
    return x + y; // 执行完这句,函数直接下班
    printf("这句代码永远不会执行!"); // 白写了
}

五、函数的 “高级玩法”:嵌套调用和链式调用

  学会了基础,咱们来玩点高级的!函数可不是孤立的,它们能互相 “合作”,效率直接翻倍。就像好几个外卖小哥一起出动的时候也会相互帮忙一样,让效率最高。

1. 嵌套调用:函数里再喊其他函数,分工明确

  就像你点外卖时,奶茶店(函数1)喊快递小哥(函数2)来取餐 —— 函数也能在内部调用其他函数.

  那让我们一起算算某一年的某一月有多少天:

// 判断闰年的函数(专门干这一件事)
int is_leap_year(int y) {
    return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}

// 计算某个月有多少天(需要用到闰年判断)
int get_days(int y, int m) {
    int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    int day = days[m];
    // 如果是闰年且是2月,多一天
    if (is_leap_year(y) && m == 2) { 
        day++;
    }
    return day;
}

int main() {
    int year, month;
    scanf("%d %d", &year, &month);
    printf("%d天\n", get_days(year, month)); // 调用get_days,它又调用is_leap_year
    return 0;
}

  这种分工就像工厂流水线:is_leap_year专门查闰年,get_days专门算天数,各司其职,代码逻辑清晰得一批!

2. 链式调用:函数的返回值直接当另一个函数的参数

  更牛的操作来了:函数的返回值可以直接 “传给” 另一个函数,就像接力赛一样:

#include <string.h>

int main() {
    // 正常写法:先算长度,再打印
    int len = strlen("abcdef");
    printf("%d\n", len);
    
    // 链式调用:一步到位!
    printf("%d\n", strlen("abcdef")); 
    return 0;
}

  strlen函数返回字符串长度,这个结果直接被printf当成参数,少写一行代码。

六、函数声明和定义:别让编译器 “迷路”

  编译器读代码是 “从上到下” 的,如果函数定义在调用后面,编译器会 “不认识” 这个函数,比如:

#include <stdio.h>

int main() {
    int sum = Add(3, 5); // 编译器:Add是谁?没见过!
    printf("%d\n", sum);
    return 0;
}

// 函数定义在调用后面
int Add(int x, int y) {
    return x + y;
}

  这时候就得用 “函数声明”—— 提前告诉编译器 “有这么个函数”,就像提前跟朋友说 “我明天要带个蛋糕来”,朋友就不会惊讶了。

函数声明的写法很简单:把函数定义的 “头” 抄下来,加个分号就行:

#include <stdio.h>

// 函数声明:提前告诉编译器有Add这个函数
int Add(int x, int y); // 参数名可省,写成int Add(int, int);也可以

int main() {
    int sum = Add(3, 5); // 编译器:哦,Add是之前说的那个函数!
    printf("%d\n", sum);
    return 0;
}

// 函数定义
int Add(int x, int y) {
    return x + y;
}

七、多文件写法:企业级规范

在公司写代码,不会把所有代码放一个文件里,而是分文件管理:

  • 头文件(.h):放函数声明、头文件包含,比如add.h
  • 源文件(.c):放函数定义,比如add.cAdd的实现,main.c写主函数。

举个例子:

  1. add.h(头文件):
    // 函数声明
    int Add(int x, int y);
  2. add.c(源文件,写函数实现):
    #include "add.h" // 包含头文件
    
    // 函数定义
    int Add(int x, int y) {
        return x + y;
    }

  3. main.c(源文件,写主函数):
    #include <stdio.h>
    #include "add.h" // 包含头文件,才能用Add
    
    int main() {
        int sum = Add(3, 5);
        printf("%d\n", sum);
        return 0;
    }

      这样分工清晰,多人协作时不会互相干扰 —— 你改add.c的实现,我改main.c的逻辑,互不影响。

八、总结:函数是代码的 “万能工具”

看到这里,你应该明白函数的好处了吧:

  1. 省时间:写一次能用 N 次,不用重复写代码。
  2. 好理解:按功能拆分代码,就像文章分段落,一眼能看懂。
  3. 易维护:改功能只改对应函数,不用动整个代码。
  4. 能协作:多文件管理,多人一起写代码不混乱。

其实函数不难, 一开始觉得难,多写几个例子就熟了。下次写代码时,别再把所有逻辑堆在main里,试着拆成一个个小函数,你会发现代码突然变清爽了!

好了,今天的函数教学就到这,赶紧去写个自定义函数试试吧~

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/hy20060720/article/details/152457897

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--