函数

函数

  1. 为完成某一功能的程序指令(语句)的集合,称为函数
  2. 在C语言中,函数分为:自定义函数,系统函数
  3. 函数又叫方法

基本语法 返回类型 函数名 (形参列表){ 执行语句...;//函数体 return 返回值;//可选 }

  1. 形参列表:表示函数的输入
  2. 函数中的语句:表示为了实现某一功能代码块
  3. 函数可以有返回值,也可以没有,如果没有返回值,返回类型 声明为 void

快速入门案例 使用函数来解决计算问题

#Include<stdio.h>
//说明
//1. 函数名 cal
//2. 有返回值 double
//3. 形参列表为 (int n1,int n2,char 	oper)
//4. 在函数中,我们使用的变量名需要和形参列表的变量名一样
double cal(int n1,int n2,char oper) {
//定义一个变量res,保存运算的结果
double res =  0.0;

switch(oper) {
case '+':
	res = n1 + n2;
	break;
case '-':
	res = n1 - n2;
	break;
case '*':
	res = n1 * n2;
	break;
case '/':
	res = n1 / n2;
	break;
default:
	printf("你的运算符号有误");
}
printf("\n%d %c %d = %.2f\n",n1,oper,n2,res);
return res;
}

void main(){
int num1 = 10;//第一个数
int num2 = 20;//第二个数
double res = 0.0;//结果
char oper = '-';//运算符

//我们又要接收两个数和一个运算符
int num3 = 60;
int num4 = 80;
char oper2 = '+';
double res2 = 0.0;

printf("使用函数解决计算任务~~");

res = cal(num1,num2,oper);
printf("\n cal函数返回的的结果是 res = %.2f",res);

printf("使用函数解决第二个计算任务~~");

res2 = cal(num33,num4,oper2);
printf("\n cal函数返回的的结果是 res2 = %.2f",res2);

}

头文件

  1. 头文件是扩展名为.h的文件,包含了C函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和C标准库自带的头文件
  2. 在程序中要使用头文件,需要使用C预处理指令#include 来引用它。前面stdio.h头文件,是C标准库自带的头文件
  3. #include 叫做文件包含指令,用来引入对应的头文件(.h文件)。#include 也是C语言预处理命令的一种。#include 的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别是在程序是由多个源文件组成的时候
  4. 建议把所有的常量,宏,系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件

工作原理图 输入图片描述

头文件快速入门 C程序相互调用函数,我们将cal 声明到文件 myfun.h,在myfun.c中定义cal 函数,当其他文件需要使用到myfun.h 声明的函数时,可以#include 该头文件,就可以使用了


myfun.h

#include<stdio.h>
//声明函数
int myCal(int n1,int n2,char oper);  

myfun.c

#include<stdio.h>
int myCal(int n1,int n2,char oper){
//定义一个变量 res,保存运算的结果
double res = 0.0;
switch(oper2){ 
	case'+':
			res2 = num3 + num4;
			break;
	case'-':
			res2 = num3 - num4;
			break;
	case'*':
			res2 = num3 * num4;
			break;
	case'/':
			res2 = num3 / num4;
			break;
	default:
			printf("你的运算符号有误");
}
printf("%d %c %d = %.2f\n",num3,oper2,num4,res2);
return res;
}

hello.c

#include<stdio.h>
//引入需要的头文件
#include"myfun.h"

	void main(){
//使用 myCal 完成计算任务

	int n1 = 10;
	int n2 = 50;
	char oper = '-';
	double res = 0.0;

	//调用 myfun.c 中定义的函数 myCal
	res = myCal(n1,n2,oper);
	
	printf("\nres=%.2f",res);

getchar();

}

头文件的注意事项和细节说明

  1. 引用头文件相当于复制头文件的内容
  2. 源文件的名字 可以 不和 头文件一样,但是为了好管理,一般头文件名和源文件名一样
  3. C语言中 include< > 与 include" "的区别 include< >:引用的是编译器的类库路径里面的头文件,用于 include" ":引用的是程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的类库路径的目录下找到该头文件,用于引用用户头文件 所以: 引用 系统头文件 两种形式都可以,<>效率高 引用 用户头文件,只能使用include" "
  4. 一个#include 命令只能包含一个头文件,多个头文件则需要多个 #include 命令
  5. 同一个头文件如果被多次引入,多次引入的效果和引入一次的效果相同,因为头文件在代码层面有防止重复引入的机制
  6. 在一个被包含的文件(.c)中又可以包含另一个文件头文件(.h) A
  7. 不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义,否则在多次引入时会引起重复定义错误

函数 调用机制

输入图片描述

调用规则(适用于java,c++,php)

  1. 当调用(执行)一个函数时,就会开辟一个独立的空间(栈)
  2. 每个栈是相互独立的
  3. 当函数执行完毕后,会返回到调用函数位置,继续执行
  4. 如果函数有返回值,则,将返回值赋给接收变量
  5. 当一个函数返回后,该函数对应的栈空间也就销毁

举例说明

输入图片描述

函数的递归调用

基本介绍

一个函数在函数体内又调用了本身,称其为递归调用

递归调用快速入门

输入图片描述

函数递归需要遵守的重要原则

  1. 执行一个函数时,就创建一个新的受保护的独立空间(新空间栈)
  2. 函数的局部变量是独立的,不会相互影响
  3. 递归必须向推出递归的条件逼近,否则就是无限递归,死龟了:)
  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁

递归课堂练习题

斐波那契数

#include<stdio.h>  
int fbn(int n){  
if(n==1 || n==2){  
return 1;  
}else{  
return fbn(n-2)+fbn(n-1);  
}  
}  

int main(){  
int n = 8;  
int res = fbn(n);  
return res;  
}

求函数值 已知 f(1)=3;f(n)=2*f(n-1)+1; 求f(n)

#include<stdio.h>  
int f(int n){  
if(n==1){  
return 3;  
}else{  
return 2*f(n-1)-1;  
}  
}  

int main(){  

int res = f(5);  
return res;  
}

猴子吃桃,每天吃一半加一个,第十天还没吃发现只剩一个

#include<stdio.h>

int peach(int day){ if(day==10){ return 1; }else{ return (peach(day+1)+1)*2; } }

void main(){ int day = 1; int res = peach(day); printf("第%d天有%d个桃子",day,res); getchar(); }


函数注意事项和细节讨论

  1. 函数的形参列表可以是多个

  2. C语言传递参数可以是值传递,也可以指针传递,又叫引用传递

  3. 函数的命名遵循标识符命名规范,首字母不能是数字,可以采用 驼峰法 或者 下划线法 getSum 或者 get_sum

  4. 函数中的变量是局部的,函数外不生效

  5. 基本数据类型默认是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值

     void f2(int n) {
     n++;
     printf("\nf2中的n=%d",n);//n=10
     }
    
     void main() {
     //函数中的变量是局部的,函数外不生效
     int n = 9;//n=9
     f2(n);
     printf("\nmain中的n=%d",n);
     getchar();
     }
    
  6. 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用(即传递指针)

     void f3(int* p){
     	(*p)++;//此处的修改会对函数外的变量有影响
     }
     void main() {
     //函数中的变量是局部的,函数外不生效
     int n = 9;
     f3(&n);
     printf("\nmain 函数中 n=%d",n);
     getchar();
     }
    
  7. C语言不支持函数重载

  8. C语言支持可变参数函数//知道即可


课堂练习题

编写一个函数 swap(int* n1,int* n2)可以交换n1 和 n2 的值

#include<stdio.h>

//说明
//1. 函数名是 swap
//2. 形参是两个指针类型 int*
void swap(int* n1,int* n2){
int temp = *n1;
*n1 = *n2;
*n2 = temp;
}

void main() {
int n1 = 1;
int n2 = 2;
swap(&n1,&n2);
printf("%d %d",n1,n2);
getchar();
}

对上面的代码执行机制分析(重要) 输入图片描述

函数参数的传递方式

基本介绍

值传递,指针传递(引用传递)

相同与不同

  1. 不管是值传递还是指针传递,传递给函数的都是变量的副本
  2. 值传递的是值的拷贝,数据越大,效率越低
  3. 引用传递的是地址的拷贝,效率高,因为数据量小

值传递和引用传递使用特点

  1. 值传递:变量直接存储值,内存通常在栈中分配 输入图片描述 默认是值传递的数据类型:基本数据类型,结构体,共用体,枚举类型

  2. 引用传递:变量存储的是一个地址,这个地址对应的空间才是真正存储数据(值) 输入图片描述 默认是引用传递的数据类型:指针和数组

  3. 如果希望函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量(*指针)


变量作用域

基本说明

所谓变量作用域(Scope),就是指变量的有效范围

  1. 函数内部声明/定义的局部变量,作用域仅限于函数内部

     void sayHello() {
     char name[] = "tom";
     printf("hello %s \n",name);
     }
     void main() {
     sayHello();
     //这里不能使用到sayHello 的 name 变量
     printf("name=%s",name);//这里将提示,没有定义name
     }
    
  2. 函数的参数,形式参数,被当做该函数内的局部变量,如果与全局变量同名,它们会优先使用局部变量(编译器使用就近原则)

     int n = 20;//函数外部定义的变量,就是全局变量
     //函数形参,会被视为 f10 的局部变量
     //说明:在局部变量和全局变量同名是,以局部变量为准(就近原则)
     void f10(int n) {
     	printf("\nn=%d",n);
     }
     void main() {
     	f10(10);//输出就是10
     }	    
    

  1. 在一个代码块,比如 for/if 中的局部变量,那么这个变量的作用域就在该代码块

       void main() {
     int i = 0;
     for(i = 0;i<10;i++){
     int k = 90;//k的作用域在for代码块中
     printf("i=%d k=%d\n",i,k);
     }
     printf("k=%d",k);
     getchar();
     }
    
  2. 在所有函数外部定义的变量教全局变量,作用域在整个程序有效


初始化局部变量

  1. 局部变量,系统不会对其默认初始化,必须对局部变量初始化后才能使用,否则,程序运行后可能会异常退出
  2. 全局变量,系统会自动对其初始化。如图所示
  3. 正确的初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量会导致一些在内存位置中已经可用的垃圾

作用域的注意事项和细节

  1. 全局变量保存在内存的全局存储区中,占用静态的存储单元,它的作用域默认是整个程序,也就是所有的代码文件,包括源文件(.c文件)和头文件(.h文件)
  2. 局部变量保存在栈中,函数被调用时才动态地为变量分配存储单元,它的作用域仅限于函数内部
  3. C语言规定,只能从小的作用域向大的中去寻找变量,而不能反过来,使用更小的作用域中的变量
  4. 在同一作用域,变量名不能重复,在不同的作用域,变量名可以重复,使用时编译器采用就近原则
  5. 有{}包围起来的代码块也拥有独立的作用域

课堂练习题 下面的代码输出什么内容

#include<stdio.h>
double price = 200.0;//全局变量
void test01(){
	printf("%.2f\n",price);//就是 全局变量:200.0
}
void test02(){//采用就近原则
double price= 250.0;//如果这里没有double,全局变量就会被修改,影响到后面程序运行的结果
	printf("%.2f\n",price");
}
void main(){
printf("main price=%.2f\n",price);//200.00
test01();//200.00
test02();//250.00
test01();//200.00
getchar();
}

#include<stdio.h>
int n =10;
void func1() {
	int n = 20;
	printf("func1 n:%d\n",n);
}

void func2(int n) {
	printf("func2 n:%d\n",n);
}

void func3(int n) {
	printf("func3 n:%d\n",n);
}

	int main() {
int n = 30;
func1();
func2(n);
func3();

{
	int n = 40;
	printf("block n:%d\n",n);
}
printf("main n:%d\n",n);
getchar();
return 0;
}

本文章使用limfx的vscode插件快速发布