文章目录
前言
在数学里面我们学过很多种函数,像什么对数函数,指数函数等等,这些让俺看到就脑阔痛的,现在都还记得我高中数学老师意气风发的把整个黑板写满,擦了又写写了擦,我只能在下面像个傻x一样白瞪眼看着他,哈哈。不过C语言中的函数是非常友善,没有数学中函数那般复杂,非常容易上手,接下来,就来学习学习C中的函数。
函数是什么?
维基百科中对函数的定义:子程序
在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组 成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
大白话:之前我们写一个加法功能是写在main函数里面的现在,把它拿出来单独写在main函数外面,就成了一个加法函数。
为什么要有函数?
随着程序的开发,所需要的功能越来越多,其中有些功能是需要重复使用,如果将代码只放在一个函数,会变得越来越复杂,不方便维护进一步开发所以将不同的功能分为各个函数,即降低复杂度又可以将单一功能重复调用不需要重复编写。
C语言中函数的分类:
- 1.库函数
- 2.自定义函数
为什么要有库函数?
C语言设计之初,只设计出语法并没有函数,随着C语言被应用于开发中,有一个A程序员要打印某个东西,他就自己写了个程序,B程序员也需要打印某句话,他自己也写个程序,然后就导致同一个项目中打印的程序就有很多个,每个人写的代码还可能不样,导致项目代码冗余,开发效率低下,也不够标椎因为每个人写的打印程序都不一样,基于以上原因C语言就把一些常用的功能实现成函数,集成库,就有库函数了。
简单的总结,C语言常用的库函数都有:
IO函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
接下来就来学习两个库函数
#include <stdio.h> #include<string.h> //strcpy();函数的使用 int main() { char arr[] = "abcdefg";//abcdefg\0 char arr2[20] = { "xxxxxxxxx"}; //将arr中的字符串拷贝到arr2中\0的ascall的值为0 strcpy(arr2, arr); printf("%s", arr2); return 0; } // memset(); int main() { char arr [] = { "hello bit"}; memset(arr,'X', 3);//替换hello前三个字符串 //1.设置内存的时候都是以字节为单位 //2.每个字节的内容都是一样value printf("%s",arr); return 0; }
注: 但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。 这里对照文档来学习上面几个库函数,目的是掌握库函数的使用方法。
如何学习库函数
需要学会查询工具/网址的使用:
MSDN工具
自定义函数
顾名思义就是程序员自己根据需求定义一个函数,之所以有自定义函数是因为库函数万能的,它只是有常用的功能不可能包含所有的功能,因为时代在进步它不可能一开始就知道我们这个时代的需求,一切都时动态变化的,所以我们需要学会自定义函数,去解决不同的需求,说白点库函数也是人自定义的,所有更加重要的是自定义函数。 自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。 函数的组成:
ret_type fun_name(para1, * ) { statement;//语句项 } ret_type 返回类型 fun_name 函数名 para1 函数参数
举一个加法函数的例子
//get_max函数的设计 int get_max(int x, int y) //xy为形参 { return (x > y) ? (x) : (y);//返回值求出来的最值 } int main() { int num1 = 10; int num2 = 20; int max = get_max(num1, num2);//num1 ,num2为实参,声明一个max的变量来接收返回值 printf("max = %d\n", max); return 0; }
在来举一个例子写一个函数可以交换两个整形变量的内容。
//错误的Swap1 void Swap1(int x, int y)//形参x,外在内存中开辟空间,去分别存储传过来的num1和num2 空间3和空间4 { int tmp = 0;//开辟空间5存储0 tmp = x; x = y; y = tmp; //这段代码就换的是以tmp为中间变量,来达到空间3和空间4的值互相交换,并没有把空间1和空间2中的值交换 } //正确的版本 void Swap2(int* px, int* py)//px和py为指针变量,储存的就是地址,指针即地址 { int tmp = 0;//开辟空间存储0 tmp = *px; *px = *py; *py = tmp; } int main() { int num1 = 1;//声明num1开辟空间存储1 空间1 int num2 = 2;//声明num1开辟空间存储2 空间2 Swap1(num1, num2); printf("Swap1::num1 = %d num2 = %d\n", num1, num2); Swap2(&num1, &num2);//去地址,传地址 printf("Swap2::num1 = %d num2 = %d\n", num1, num2); return 0;
先看看美女在来学习
函数的参数
实际参数(实参)
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内 存单 元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
上面 Swap1 和 Swap2 函数中的参数 x,y,px,py 都是形式参数。在main函数中传给 Swap1 的 num1 , num2 和传 给 Swap2 函数的 &num1 , &num2 是实际参数。 这里我们对函数的实参和形参进行分析:
内存解析
这里可以看到 Swap1 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。 所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。
函数的调用:
- 1.传值调用
- 2.传址调用
传值调用 函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操 作函数外部的变量。(交换两个变量的例子)
.函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
嵌套调用
#include <stdio.h> void new_line() { printf("hehe\n"); } void three_line() { int i = 0; for(i=0; i<3; i++) { new_line(); } } int main() { three_line(); return 0; }
函数可以嵌套调用,但是不能嵌套定义。
链式访问
链式访问 把一个函数的返回值作为另外一个函数的参数
int main() { char arr[20] = "hello"; int ret = strlen(strcat(arr,"bit"));//这里介绍一下strlen函数 printf("%d\n", ret); return 0; } #include <stdio.h> int main() { printf("%d", printf("%d", printf("%d", 43))); //结果是啥? //注:printf函数的返回值是打印在屏幕上字符的个数 return 0; }
函数的声明和定义
函数声明:
- 1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
- 2.函数的声明一般出现在函数的使用之前。要满足先声明后使用。
- 3.函数的声明一般要放在头文件中的。
函数定义:
函数的定义是指函数的具体实现,交待函数的功能实现。
test.h的内容 放置函数的声明
int Add(int x, int y);
test.c的内容 放置函数的实现
int Add(int x, int y) { return x + y; }
Demo.c调用函数
#include "Add.h"//引用头文件 int main() { int num1 = 0; int num2; scanf("%d %d", &num1, &num2); int sum = Add(num1, num2); printf("%d", sum); return 0; }
还可以直接写到一个文件里去
int Add(int x, int y) { return x + y; }//Add函数需要写到前面,不然就得先声明 int main() { int num1 = 0; int num2; scanf("%d %d", &num1, &num2); int sum = Add(num1, num2); printf("%d", sum); return 0; }
建议分文件写
函数递归
什么是递归? 程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
递归的两个必要条件
存在限制条件,当满足这个限制条件的时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件。
史上最简单的递归
int main() { printf("你是猪!\n"); main(); return 0; }//死递归死循环,栈溢出
递归实现接受一个整型值(无符号),按照顺序打印它的每一位。
void print(int n) { if (n > 9) { print(n / 10);//反复调用自己 } printf("%d ", n % 10); } int main() { int num = 0; scanf("%d", &num);//1234 print(num); return 0; }
图解
递归:就是自己调自己(递推),调用函数需要返回,在哪里调用就返回到哪里(回归)
结束语
有时候,你也许得为了某个特定的事物生活,这样才能克服生活中其他的一切。 – 弗雷德里克·巴克曼 《熊镇2》