重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在数学函数是一种对应关系,C语言里的函数和数学中的函数还是极其相似的
专注于为中小企业提供成都网站制作、网站建设、外贸网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业潼关免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了超过千家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。(1)维基百科对函数的定义
①维基百科:子程序 - 维基百科,自由的百科全书 (shenyinjise.icu)
②子程序。是大型程序中的某部分代码,由一个或多个语句组成。负责完成某项特定的任务,而且相较于其他代码具有相对的独立性。
C语言中的函数,一般会有输入参数并由返回值,提供对过程的封装和细节的隐藏,这些代码通常集成为一个软件库。
2、C语言中函数的分类(“库函数”和“自定义函数”) (1)库函数①C语言自带的函数,可以直接使用,方便程序员进行软件开发
可以在这个网站里面查找: https://cplusplus.com/
或者C/C++官网: cppreference.com(中文版: cppreference.com)
或者使用工具MSDN,可以离线查看,但是更新度不够
②使用库函数,需要包含#include对应的头文件
③常用的库文件如下:
不可能所有的库函数都符合我们的需求,因此我们还需要自己写符合自己需求的函数,所以学会写自定义函数是最为重要的!
①函数必须有这些构成:函数的返回类型(注意这不是函数的类型)、函数名、函数参数列表、函数体
②伪代码:函数的结构
返回类型 函数名(参数列表)//注意返回指和参数列表可以是“空”,即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)实际参数(实参)真实传给函数的参数,实参可以是:常量、变量、表达式、函数等。
实参在进行函数调用的时候,必须有确定的值,然后传值给形参使用。
①简单来讲就是函数定义后,函数名后面括号的内容中的变量,只有在函数被调用的时候,实参传值给形参时,形参才会有值(即“才会实例化“,“才会分配内存单元”,因此就是形式上的参数)。
②更简单理解就是,“形参”是“实参”的临时拷贝,对形参的改动和修改是不会影响到实参的。
③如果需要通过形参来改动实参,则不能只是传递值,而是应该传递地址!
④另外形式参数在函数调用完后就自动销毁了,因此形式参数只有在函数内部才是有效的
⑤函数中的形式参数是在栈中开辟空间的
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)函数的声明①是为了告诉编译器存在一个自定义的函数,需要声明函数的结构
(函数名字、函数参数、返回类型)
②一般在使用函数之前声明函数
③函数的声明一般放在头文件中
交代函数的具体实现方式
(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静态库的产生和使用
递归适当使用会大大减少代码量
(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个数就会很吃力了,运算时间很久
#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元起,快前往官网查看详情吧