03-C语言函数

函数

函数的分类

  • 在C语言中可从不同的角度对函数分类

  • 从函数定义的角度看,函数可分为库函数和用户定义函数两种

    • 库函数: 由C语言系统提供,用户无须定义
    • 用户定义函数:由用户按需编写的函数。
  • 从函数执行结果的角度来看, 函数可分为有返回值函数和无返回值函数两种

    • 有返回值函数: 此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。(必须指定返回值类型和使用return关键字返回对应数据)
    • 无返回值函数: 此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。(返回值类型为void, 不用使用return关键字返回对应数据)
  • 从主调函数和被调函数之间数据传送的角度看,又可分为无参函数和有参函数两种

    • 无参函数: 在函数定义及函数说明及函数调用中均不带参数。主调函数和被调函数之间不进行参数传送。
    • 有参函数: 在函数定义及函数说明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)
  • 为什么要定义函数?

    • 首先,如果不定义函数:
      • 重复代码冗余代码过多;
      • 需求发生改变时;需要修改很多代码
    • 如果定义函数
    • 将某些代码封装起来,方便以后的使用;
    • 提高代码的复用性;
    • 使代码更加简洁;
    • 便于维护;

函数的定义

  • 格式:
// 标注  有参数有返回值函数的定义
返回值类型 函数名称(形参列表){
    被封装的函数;
    返回值;
}
例子:

int main(){
    printf("hello world");
    return 0;
}

// 获取最大值
int getMax(int num1, int num2){
    int max = num1 > num2 ? num1 : num2;
    return max;
}


// 无参数无返回值的函数定义:
void 函数名(){
    函数体;
}

// 无参有返回值函数定义
返回值类型 函数名(){
    函数体;
    return 值;
}

// 有参无返回值函数定义
void 函数名(参数类型 形式参数1, 参数类型 形式参数2, ...){
    函数体;
}


  • 初学者如何定义函数
    • 1.确定函数的名称(给函数起一个有意义的名称,让调用者见名知意)
    • 注意点: 函数名称也是标识符的一种,所以也需要遵守标识符的命名规则: 字母,数字,下划线的驼峰命名;
    • 2.确定形参列表(告诉调用者,调用时是否需要传递一些辅助的数据);
      • 注意点: 形参列表的格式 (数据类型 变量名称, 数据类型 变量名称, ...)
      • 数据类型 变量名称 可以有零个或多个;
    • 3.确定函数的功能代码(也就是要封装的代码)
    • 4.确定返回值和返回值的类型
    • return 的作用之一就是将后面的数据返回给函数的调用者;
#include <stdio.h>
int getAverage(int num1, int num2){
    int avg = (num1 + num2) / 2;
    return avg;
}
int main()
{
    int a = 10;
    int b = 20;
//    int res = (a + b) / 2;
    int res = getAverage(a, b);
    printf("average = %i\n", res);
    return 0;
}

// 注意点: main函数的作用域和getAverage作用域不同

image

函数的参数和返回值

  • 函数的参数:
    • 函数可以有参数,也可以没有参数;
    • 函数的参数可以是零个或多个;
    • 函数可以有返回值;也可以没有返回值;
    • 注意:如果函数没有返回值,那么返回值类型写void就行;
    • 函数的返回值类型可以是所有C语言类型
  • 形式参数:
    • 定义函数时,函数名后面小括号()中定义的变量成为形式参数,简称形参
    • 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元
    • 因此,形参只有在函数内部有效,函数调用结束返回主调函数后则不能再使用该形参变量
int getSum(int num1, int num2){  // 形式参数
    return num1+num2;
}
  • 实际参数:
    • 调用函数时,传入的值称为实际参数,简称实参;
    • 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参
    • 因此应预先用赋值,输入等办法使实参获得确定值
int main(){
    int a = 99, b =100;
    int sum = getSum(a, b); // 实际参数
}
  • 返回值的注意点:
    • 如果函数没有编写返回值类型,那么默认就是int类型!
    • 如果函数的返回值类型和实际return返回的类型不同,那么会隐式(自动)转换为返回值类型;
test1(){
    return 3.14;
}

// 返回值为3
//如果函数的返回值类型和实际return返回的类型不同,那么会隐式(自动)转换为返回值类型
int test2(){
    return 3.14;  // double
}
// 返回值3

int main()
{
    double res = test1();
    printf("res = %lf",res); // 3.000000
    return 0;
}

  • 函数的注意点:
    • 1.函数的名称不能相同;
      • 哪怕返回类型不同,函数名称也不能相同
      • 哪怕形参列表不同,函数名臣也不能相同
    • 2.函数不能嵌套定义,哪怕编译器允许的,也不建议这么干;
    • 3.如果是基本类型的数据作为函数的参数 ,那么在函数内修改形参, 不会影响外面实参的值;
    • 4.在函数的内部不能定义和形参同名的变量
    void test(){
        int num = 666; // 这么定义变量会报错
        printf("num = %i\n", num);
    }
    
    • 函数不要嵌套定义

函数的声明

  • 函数的定义可以写在函数调用之前,也可以写在函数调用之后,但是要进行函数的声明;

  • 标准的写法:函数的声明只需要将函数定义{前面的代码拷贝即可;

  • 函数声明,就是在函数调用之前告诉系统, 该函数叫什么名称, 该函数接收几个参数, 该函数的返回值类型是什么

  • 函数的声明只需将函数定义{前面的代码拷贝即可

  • 声明函数的注意点:

    • 1.函数的声明只要写在调用之前

      • 也就是说,函数的声明,可以写在函数的外面
      • 也可以写在函数里面
    • 2.函数声明的时候,形参可以不用在指定名称

    • 3.如果函数的返回值类型是整型,那么可以不用编写函数声明(但是还是建议写上);

    • 由于函数声明仅仅是为了告诉编译器,我们有一个什么函数,所以函数的声明是可以声明多次,但是函数的的实现只能一次;

    • 总结:

      • 1.以后但凡函数的定义写在函数调用之前,一定要在调用之前编写函数的声明;
      • 2.函数的声明只会告诉系统函数叫什么,接收几个什么类型参数,返回值是什么类型即可;
      • 3.一般情况下我们会将函数的声明写到函数的外面,而不会写到函数的里面;

main函数分析

  • main函数分析

    • main是函数的名称, 和我们自定义的函数名称一样, 也是一个标识符
    • 只不过main这个名称比较特殊, 程序已启动就会自动调用它
  • return 0;的含义:

    • 告诉系统main函数是否正确的被执行了
    • 如果main函数的执行正常, 那么就返回0
    • 如果main函数执行不正常, 那么就返回一个非0的数
  • 返回值类型:

    • 一个函数return后面写的是什么类型, 函数的返回值类型就必须是什么类型, 所以写int
  • 在企业开发中,我们是可以通过控制台(cmd)在运行程序的时候给main函数传递参数就可以了

int main(int argc, const char *argv[])
{
    
    // 代表数组中有一个元素
    // 代表数组中有两个元素
    // 默认情况下, 系统在调用main函数的时候, 会给argv这个数组中存放一个元素
    
    printf("argc = %i\n", argc);
    printf("argv[0] = %s\n", argv[0]);
    printf("argv[1] = %s\n", argv[1]);
    printf("argv[2] = %s\n", argv[2]);
    return 0;
}

递归函数

- 递归: 自己搞自己
- 递归可以实现循环的功能,但是递归的性能比循环差很多
- return 只能返回给调用者
// do - while 写法:
int num = -1;
     do{
          // 提醒用户如何输入数据
         printf("请输入一个正整数,以回车结束;\n");
         // 接收用户输入的数据;
         scanf("%i", &num);
     }while( num < 0);

     // 输出
     printf("%i",num);
     
     
// 递归写法

int test(){
    // 定义用户保存的变量
    int num = -1;
    // 提示用户如何输入数据
    printf("请输入一个正整数,以回车结束;\n");
    // 接收用户输入的数据
    scanf("%i", &num);
    if(num < 0 ){
        // 用户输入不合法
        return test();
    }
    return num;
}
     
image
  • 循环和递归的区别

    • 能用循环实现的功能,用递归都可以实现;
    • 递归常用于"回溯", "树的遍历","图的搜索"等问题
    • 但代码理解难度大,内存消耗大(易导致栈溢出), 所以考虑到代码理解难度和内存消耗问题, 在企业开发中一般能用循环都不会使用递归
  • 设计一个函数用来计算B的n次方

#include <stdio.h>
int pow(int b, int n);
int main()
{
    /*
     * 编写一个函数实现    B的n次方;
     * B^n
     *
     * b^0 = 1;
     * b^1 = b;
     * b^2 = b*b;
     * b^3 = b*b*b;
     *
     * b^0 = 1;
     * b^1 = b^0 * b;
     * b^2 = b^1 * b;
     *
     * 规律:  B^n = b^(n-1) * b;
     *
    */

    // 定义用户输入的变量
    int B = -1, n = -1;
    // 提示用户输入
    printf("请输入两个数,代表B^n;以逗号隔开\n");
    // 接收用户输入
    scanf("%i,%i", &B, &n);
    // 接收结果
    int res = pow(B, n);
    printf("res = %i\n",res);
    return 0;
}

// 传入格式: B^n
int pow(int b, int n){
    if(n == 0){
        return 1;
    }else{
        return pow(b, n-1) * b;
    }
}


  • 用递归法求N的阶乘

#include <stdio.h>
int jieCheng(int n);
int main()
{
    /*
     * 利用递归实现阶乘
     *
     * 3! = 3*2*1;
     * 2! = 2*1;
     * 1! = 1;
     *
     * 规律  n = (n-1)! * n;
     *
    */

    // 定义变量  n;
    int n = -1;
    // 提示输入n
    printf("请输入N的阶乘:\n");
    // 接收用户输入的n
    scanf("%i", &n);
    // 保存结果
    int res = jieCheng(n);
    // 输入结果
    printf("%i! = %i", n, res);
    return 0;
}

int jieCheng(int n){
    if(n==1){
        return 1;
    }else {
        return jieCheng(n-1) *n;
    }
    return n;
}

  • 有5个人坐在一起,问第5个人多少岁?他说比第4个人大两岁。问 第4个人岁数,他说比第3个人大两岁。问第3个人,又说比第2个 人大两岁。问第2个人,说比第1个人大两岁。最后问第1个人, 他说是10岁。请问第5个人多大?
#include <stdio.h>
int getAge(int n);
int main()
{
    /*
     * 有5个人坐在一起,问第5个人多少岁?他说比第4个人大两岁。
     * 问 第4个人岁数,他说比第3个人大两岁。
     * 问第3个人,又说比第2个 人大两岁。
     * 问第2个人,说比第1个人大两岁。
     * 最后问第1个人, 他说是10岁。
     * 请问第5个人多大?
     *
     * 第一个人:  10;
     * 第二个人:  10 + 2;
     * 第三个人:  10 + 2 + 2;
     * 第四个人:  10 + 2 + 2 + 2;
     * 第五个人:  10 + 2 + 2 + 2 + 2;
     *
     * 规律:(n -1) + 2;
     *   其中第几个人是n, 10就是n的默认值;
     *
     *
     *
    */


    // 定义第n个用户输入的变量
    int n = -1;
    // 提示用户输入
    printf("请输入一个整数:\n");
    // 接收输入
    scanf("%i", &n);
    // 定义岁数
    int age = getAge(n);
    // 输出
    printf("age = %i", age);
    return 0;
}


// 递归实现年龄
int getAge(int n){
    if(n == 1){
        return 10;
    }else{
        return getAge(n-1) + 2;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 1、c语言的函数有以下特点: (1)才源程序由函数组成,一个主函数main()+若干其他函数 C程序中的函数类似文...
    滕王阁序阅读 1,330评论 0 6
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,585评论 1 19
  • C语言函数(一) 谨记 短暂的人生中,注定有很多人陪伴着你度过,陪你一起欢呼,陪你一起伤心流泪,陪你一起走过一条又...
    长风留言阅读 2,244评论 1 7
  • 函数基本概念 C源程序是由函数组成的例如: 我们前面学习的课程当中,通过main函数+scanf函数+printf...
    极客江南阅读 1,334评论 1 14
  • 函数基本概念 C源程序是由函数组成的例如: 我们前面学习的课程当中,通过main函数+scanf函数+printf...
    极客学院凯阅读 221评论 0 1