C语言入门知识——(1)函数基础-创新互联

1、什么是函数

在数学函数是一种对应关系,C语言里的函数和数学中的函数还是极其相似的

专注于为中小企业提供成都网站制作、网站建设、外贸网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业潼关免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了超过千家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。(1)维基百科对函数的定义

在这里插入图片描述
①维基百科:子程序 - 维基百科,自由的百科全书 (shenyinjise.icu)
②子程序。是大型程序中的某部分代码,由一个或多个语句组成。负责完成某项特定的任务,而且相较于其他代码具有相对的独立性。

(2)另外的补充说明

C语言中的函数,一般会有输入参数并由返回值,提供对过程的封装和细节的隐藏,这些代码通常集成为一个软件库。

2、C语言中函数的分类(“库函数”和“自定义函数”) (1)库函数

①C语言自带的函数,可以直接使用,方便程序员进行软件开发
可以在这个网站里面查找: https://cplusplus.com/
或者C/C++官网: cppreference.com(中文版: cppreference.com)
或者使用工具MSDN,可以离线查看,但是更新度不够
②使用库函数,需要包含#include对应的头文件
③常用的库文件如下:

  • IO函数
  • 字符串操作函数
  • 字符处理函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 错误消息
  • 功能函数
  • windows
  • 其他库函数<……>
(2)自定义函数

不可能所有的库函数都符合我们的需求,因此我们还需要自己写符合自己需求的函数,所以学会写自定义函数是最为重要的!
①函数必须有这些构成:函数的返回类型(注意这不是函数的类型)、函数名、函数参数列表、函数体
②伪代码:函数的结构

返回类型 函数名(参数列表)//注意返回指和参数列表可以是“空”,即void的情况
{函数体(具体代码);
}//如果忘记写返回类型,则默认返回一个整数,但不写返回类型是一个坏习惯!

③一些例子
例子1:比大小的函数

int MAX(int x, int y)//自定义“相加”函数
{return (x>y)?(x):(y);//使用了三目操作符
}
int main()//主体函数
{int num_1 = 10;
    int num_2 = 100;
    int max = MAX(num_1, num_2);
    printf("max = %d\n",max);
    return 0;
}

例子2:查找闰年

int is_leap_year(int y)//自定义“判断是否为闰年”函数
{return ((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0;
}

int main()//主体函数
{int y = 0;
    for (y = 1000; y<= 2000; y++)
    {//判断y是否为闰年
        //如果是闰年返回1 
        //如果不是闰年返回0
        if (is_leap_year(y))
        {printf("%d ", y);
        }
    }
    return 0;
}

④一般来说要先在主函数(主程序)里面写如何去用、在哪里用这个函数,再去写自定义函数的具体实现会比较好一点。(这其实是TDD思想,TDD指“测试驱动开发(Test-Driven Development)”)

3、函数的参数 (1)实际参数(实参)

真实传给函数的参数,实参可以是:常量、变量、表达式、函数等。
实参在进行函数调用的时候,必须有确定的值,然后传值给形参使用。

(2)形式参数(形参)

①简单来讲就是函数定义后,函数名后面括号的内容中的变量,只有在函数被调用的时候,实参传值给形参时,形参才会有值(即“才会实例化“,“才会分配内存单元”,因此就是形式上的参数)。
②更简单理解就是,“形参”是“实参”的临时拷贝,对形参的改动和修改是不会影响到实参的。
③如果需要通过形参来改动实参,则不能只是传递值,而是应该传递地址!
④另外形式参数在函数调用完后就自动销毁了,因此形式参数只有在函数内部才是有效的
⑤函数中的形式参数是在栈中开辟空间的

(3)有关函数参数易错题
int founction((a1, a2), (a3, a4), a5, a6);//函数有几个参数呢?

实际上应该是4个参数,(a1, a2)和(a3, a4)是逗号表达式,他们都各有一个结果,分别为a2和a4,故实际上函数的参数为a2,a4,a5,a6四个参数

(4)函数参数个数设置得越少越好

不然用户使用的时候,还得搞清楚每个参数的意义,并且还要输入很多参数才能使用

4、函数的调用(使用函数交换两个数字) (1)传值调用

函数的形参和实参分别占有不同的内存,对形参的修改不会影响实参。
如果写成下面这个代码就会交换失败(本质就是形参和实参的问题)

//一个典型的例子(写一个错误交换两个数的函数)
void function(int a, int b)//这里自定义函数只是临时拷贝(传过来)num_1和num_2两个数的值,所以下面只是x和y的值交换了,而num_1和num_2却没有进行实际上的交换
{int t = 0;
    t = a;
    a = b;
    b = t;
}

int main()
{int num_1 = 1;
    int num_2 = 2;
    printf("%d %d\n",num_1, num_2);
    
    function(num_1, num_2);//使用交换函数
    printf("%d %d\n", num_1, num_2);
    return 0;
}//这个函数通过编译器的调试功能会更加清晰看到
(2)传址调用
//一个典型的例子(写一个正确交换两个数的函数)
void function(int *pa, int *pb)
{int t = 0;
    t = *pa;
    *pa = *pb;
    *pb = t;
}
int main()
{int num_1 = 1;
    int num_2 = 2;
    printf("%d %d\n", num_1, num_2);
    
    function(&num_1, &num_2);//使用交换函数
    printf("%d %d\n", num_1, num_2);
    return 0;
}

总之函数的调用无论是传值还是传址都有利弊,看情况选择传值方式。从功能上传址调用比较强大,但是容易出现更多问题。

(3)高内聚低耦合

函数设计应该追求“高内聚低耦合”:在函数体内实现修改,尽量不要对外部产生影响,否则代码不便修改

5、函数的“嵌套调用”和“链式访问” (1)嵌套调用(在一个函数里调用了另一个函数)
#includevoid function_1()
{printf("aaaaa\n");
}
void function_2()
{int i = 0;
    for(i=0; i<3; i++)
    {function_1();//调用了另外一个函数
    }
}
int main()
{function_2();
    return 0;
}

注意:函数可以嵌套调用但是不能嵌套定义(一个A函数里面又定义了一个B函数,但是可以在A定义里面调用一个已经定义好的函数B(或者B定义里面调用一个已经定义好的函数A))

//以下“嵌套定义”是不被允许的!
返回类型 f1(参数列表)
{//一些代码
    返回类型 f2(参数列表)
    {//一些代码    
    }
    //一些代码
}
(2)链式访问(把一个函数的返回值作为另一个函数的参数)
//链式访问例子1
#include#includeint main()
{printf("%d\n", strlen("abcd"));
    return 0;
}
//链式访问例子2
#includeint main()
{printf("%d",printf("%d",printf("%d",43)));//printf返回值是读取到字符的个数
    return 0;
}//结果为4321这四个字符,注意不是四千三百二十一,只是单纯的输出4、3、2、1这四个字符
6、函数的声明和定义
//实际上函数的声明和变量的声明很像
#includeint a;变量的声明
int main()
{printf("%d\n",a);//变量的使用
    return 0;
}
int a = 10;//变量的定义
//这种写法也是可以正常打印出来10的
(1)函数的声明

①是为了告诉编译器存在一个自定义的函数,需要声明函数的结构
(函数名字、函数参数、返回类型)
②一般在使用函数之前声明函数
③函数的声明一般放在头文件中

(2)函数的定义

交代函数的具体实现方式

(3)教科书里常见函数声明的使用:
#includeint Add(int x, int y);//函数的声明

int main()
{int a = 1, b = 2;
    printf("%d", Add(a, b));//函数的使用
    return 0;
}

int Add(int x, int y)//函数的定义
{return x + y;
}
//这种写法虽然对,但是实际情况一般不会这么写,函数的声明一般放在头文件中,函数的实现则放到另外一个专门用来自定义函数的源文件中
(4)实际上的工程里常见的,函数声明较多的写法是:
//头文件add.h中
#ifndef __ADD_H__
#define __ADD_H__

int Add(int x, int y);//函数的声明

#endif
//函数定义源文件add.h
int Add(int x, int y)//函数的定义
{return x + y;
}
//程序主体文件main.h
#include#include "Add.h"
int main()//主函数
{int a = 1,b = 2;
    printf("%d", Add(a, b));//函数的使用
    return 0;
}
(5)一是适合分工、二是有保密措施(静态库.lib的使用)

①总不可能一堆程序员挤在一个电脑屏幕上写代码吧?多文件使得有程序员得以有效分工,每一个程序员写各种工作的头文件和源文件,最后再开始整合比较好
②使用VS产生静态库详细步骤,见我另外一篇博客:VS静态库的产生和使用

7、函数递归(递推与回归)

递归适当使用会大大减少代码量

(1)什么是递归

函数“直接”或者“间接”调用自己的一种编程技巧,它通常把一件“大事”化成“小事”来进行求解,函数不断递推出去,然后再不断地回归,组合成“递归”一词。
①最简单的递归

//最简单的递归,main函数自己调用自己
#includeint main()
{printf("abcd\n");
    main();
}//不断打印abcd此时main将不断消耗栈区的空间,最后栈溢出,程序崩溃

②按照顺序打印一个数的每一位数字字符

#includemy_printf(unsigned int x)
{if(x >9)
    {my_printf(x / 10);
        printf("%u ", x % 10);
    }
    else
    {printf("%d ", x);
    }
}
//大事化小
//my_printf(1234)
//my_printf(123)4
//my_printf(12)34
//my_printf(1)234
int main()
{unsigned int number = 0;
    scanf("%u",&number);
    my_printf(number);
    return 0;
}

理解递归函数可以像上面一样大事化小,或者是画多个图来理解
③自定义一个函数,实现和strlen函数一样的功能

#includemy_strlen(const char *str)
{if(*str == '\0')
    {return 0;
    }
    else
    {return (1 + my_strlen(str+1));
    }
}

int main()
{char arr[10] = "abcdef";
    printf("%d", my_strlen(arr));

    return 0;
}

④第n个斐波那契数列(不考虑溢出的情况)

#includeint fun(int n)
{if (n<= 2)
        return 1;
    else
        return fun(n - 1) + fun(n - 2);
}
int main()
{int n = 0;
    while (scanf("%d", &n) == 1)
    {printf("第%d个斐波那契数列为%d\n", n, fun(n));
    }
    return 0;
}

⑤阶乘的实现(不考虑溢出的情况)

#includeint fun(int n)
{if (n<= 1)
    {return 1;
    }
    else
    {return n * fun(n - 1);
    }
}
int main()
{int n = 0;
    scanf("%d", &n);
    printf("%d", fun(n));
    return 0;
}
(2)递归的必要条件

①存在限制条件(不能像上面main自己调用自己一样,否则迟早栈溢出(stack overflow))让递归停下
②每次递归都要接近这个条件,不能原封不动
递归有可能大量消耗栈空间,计算量极大,有的时候反而效率低,例如:求斐波那契数列的时候求第50个数就会很吃力了,运算时间很久

(3)迭代(循环)方式代替递归,斐波那契数列的另外一种写法
#includeint function(int n)//用递归的方法求斐波那契数列
{int a=1;
    int b=1;
    int c=1;
    while(n >= 3)
    {c = a + b;
        a = b;
        b = c;
        n--;
    }
        return c;
}
int main()
{return 0;
}
#includeint function(int n)
{int a = 1;
    while(n >1)
    {a *= n;
        n--;    
    }
    return a;
}
int main()//利用迭代求n的阶乘
{int n = 0;
    scanf("%d", &n);
    printf("%d", function(n));
    return 0;
}
(4)何时选递归

看情况,那种方便少缺陷就选哪种,没必要一定要写出递归(当然递归能大大减少代码量)。况且迭代(循环)的方法会更加清晰。

8、函数如何返回多个值

(1)使用多个全局变量,在函数内部改变,则相当于函数返回了多个值
(2)返回一个数组指针,就可以通过数组返回多个值
(3)形参使用多个指针变量,在函数体内部解引用,改变指针指向的值
唯独不能单用return,只能返回一个值,即一个函数只能返回单个结果,不能是多个

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧

当前名称:C语言入门知识——(1)函数基础-创新互联
网站路径:https://www.cdcxhl.com/article6/dijhog.html

成都网站建设公司_创新互联,为您提供关键词优化外贸网站建设商城网站网页设计公司网站收录网站排名

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都网页设计公司