chapter 7 用函数实现模块化设计

7.1函数

7.1.1 函数是什么?

什么是程序,一个C程序就是由很多很多个源程序文件构成,而每个源程序文件都是由预处理指令 ,全局 数据声明 , 加上一个又一个函数构成;

函数就是用来实现某一个特定功能的东西;而函数名就是给这一功能去起一个名字;

函数是相互平行的概念;彼此间是等价得;不存在主次之分;但是函数不是不可以写在其他函数里面;可以;;这叫做函数的嵌套调用;不管是什么函数都是可以相互调用;若干次 ;任意位置;当然有一个函数不能被其他函数调用 这就是主函数main ;他是被操作系统直接调用;函数不能被嵌套定义;

程序在编译得过程中;只会从main 函数中开始执行;一直到main结束 ;其中遇到那个就调用哪个;当遇到他得时候 主函数不会停下来;他和被调用函数一起;同时进行处理;平行线

7.1.2 那我们有哪些函数呢 ??

无非就两种 ;一个是库函数;还有一个是用户自己定义的函数;
至于库函数就不说了;就是已经定义好了的;你只要会用就完了;
那自己定义的呢;他就是得自己写;我把它分成两类 ;
要返回一个函数值;这个叫做有参函数;
不需要输出返回值;那么叫做无参函数;
调用时候不一定需要输入值;这不重要;我可以输入值的时候返回一个数字,也可以不输入一个数值,也能返回一个数值;同理,我输不输入数值对无参也是一样受用;
具体咱们举例子;
无参函数;

#include <stdio.h>
int main (){
	void print_star();
	void print_message();
	print_star();
	print_message();
	print_star();
	return 0;
}

void print_star()
{
	printf("******************\n"); 无参函数 无返回值
	
}

void print_message()
{
	printf("wu mingda dashuaibi!\n");  无参函数 无返回值
}

在这里插入图片描述有参函数:

#include <stdio.h>
int main(){
	int max(int x,int y);
	int a,b,c;
	scanf("%d,%d",&a,&b);
	c=max(a,b);
	printf("max=%d\n",c);
	return 0;
	}
int max(int x,int y){  有参函数 有返回值 有输入值 
	int z;
	if(x>y)z=x;
	else z=y;
	return(z);
	} 

在这里插入图片描述

7.2函数定义

7.2.1 怎么定义一个函数;有参 无参函数 分别怎么定义?

定义一个函数;首先我们直接看看定义函数得形式;
举例子;
类型名 函数名 (形式参数)
{
函数体 (执行部分和声明部分)
}

咱们看看就是一般而言函数定义的话;你是不是需要一个函数的名字就是定义函数名;前面说了,其次对于函数要么属于能返回函数值得;或者不能返回函数值得;如果是前者的话;那么就是无参函数 ;如果能返回一个数值;那么可以称呼为有参函数值;同理而言对于能否返回值且返回值是什么类型得;你是不是需要定义;这就是类型名;对于没有返回函数值的函数你应该定义为void 对于能返回函数值得你应该 对其能返回函数值得类型进行一个定义;比如int long longlong float ;你应该有一个具体得说法;还有就是如果是需要输入数据进去得那种 ;你还需要定义一个形式参数;对于这个参数你还需要就是定义他的数据的类型;比如来说int a;具体说形式参数与实际参数得关系 ;这儿先不慌说太多;那就是形式参数其实只是一个可有可无的东西;但是在定义函数中这个参数名字必须给到,int x;
总的来说 对于函数 而言你需要定义如下
函数返回值类型 ;(如果没有 void )
函数名字;
形式参数类型 (形式参数);
函数体;

对于如此即使还有一个特殊得工程上常常定义一些空函数;为什么;就是比如写代码就是写到这;我知道要在这儿一串比较复杂得代码 但是把 还写不出来;不知道要写什么 ;我可以一些空函数放在这;然后就是随时就能回来用它;加以补充;而且写在这儿;也不影响程序的运行;应为他相当于一个空语句’不会对程序产生任何其他得影响;

7.2.2 那么咱们看看这个空函数怎么去写;

写在这儿 直接
int main(){
......
 _gaidengshuxue wumingda();
.......
}

void gaidengshuxue wumingda(){
}
这就是一个空语句;你只带怎么用就行了;怎么写;

这就是函数得定义;

7.2.3 区分一下#include<> 与#include""的区别;

#include<>直接从编译器自带的函数库中寻找文件
#include""是先从自定义的文件中找 ,如果找不到在从函数库中寻找文件

如果是自己写的头文件 建议使用#include“”
< >引用的是编译器的类库路径里面的头文件
" "引用的是你程序目录的相对路径中的头文件

如果使用" ",它是会先在你项目的当前目录查找是否有对应头文件
如果没有,它还是会在对应的引用目录里面查找对应的头文件
意思就是,使用#include "stdio.h"如果你项目目录里面,没有stdio.h这个头文件,它还是会定位到库路径里的头文件
stdio.h 等头文件中包含了函数定义和申明;

7.3 调用函数

7.3.1 函数调用的形式

调用的时候就是函数名字加上函数的实参;像那种无参数就空着;具体形式;
函数名 (实参表列)
这就是调用的基本格式;完了就是调用这种函数具体用在代码中分为三种情况;
首先,就是函数调用语句;就是在基本的函数调用的一般格式的后面加上一个分号即可;

for example : age(); age(1,4);

其次就是,函数表达式 ;因为对于大部分的函数而言返回的都是一个有实际的值;而这个值往往可以放在表达式中;构成函数表达式;
举个例子:c=max(a,b);
最后就是;刚才说过有参函数调用后相当于一个数值;那么这个数值是可以放在函数中再次当作参数使用的;所以第三种就是** 函数参数**;

m=max(a,max(9,0));

7.3.2 形式参数 实际参数;

在调用函数时侯 分为形式参数实际参数;形式参数就是你定义的函数括号里面的参数 ;这个在定义的时候是可以有可以没有的 ;这只是形式;只是一个临时的东西;而实际参数而言;他是主函数里面确实存在的;他放在调用的时候的括号里面;它可以是表达式 ;数值 ;函数参数等可以表示一个参数的任何表现形式;

age(a,b);
int age(int x,int y){
...
}

这里面 a.b 就是实际参数 ;x,y就是 形式参数;
如果实际参数和形式参数定义的类型不同该怎么办;那就是先进行数据转换来;前面学过的;

7.3.3 函数调用的过程

函数调用这怎样处理的 ;首先实参 他是有储存单元的;max(a,b);在这里面 a,b都有自己的储存单元;同时 对于形式参数 ;int max(int x,int y)而言 x, y 也是有储存单元的 不过吧 他是临时的 就是他仅仅就是一个 用完自动消散的一个储存单元; 具体的执行过程 ;首先 就是实参储存单元的 数值直接给形式参数 ;在定义的函数体中 形式参数作为一种数值去进行各种处理 操作 数值的变化 ;但无论怎么变化 都不会影响实际参数的值 ;最后执行完结之后 ;调用函数会返回一个数值出来; 这是调用函数在这一次的使命就结束了;那么同理而言;形式参数作为一个单独的储存单元的使命就结束了;那么这个储存单元就临时解散了;
同时这里说了 调用函数会返回一个值; 要用到 return ;;;如果不返回呢 那么这里面就不能用return ;
==??==这里面有一个问题没有解决
c=isdd(a,a+b);这里面a+b作为实参那么他是单独又成为一个储存单元么;
首先 a+b 不能代表变量 ;只是一个表达式 其意义就是数值;而这个数值是放在参数的单独储存空间的;形参有一个 定义的么 实参的话 就是单独创造出来的;也即是说表列是单独有自己的储存空间的;

7.3.4 函数的返回值

作为一个调用函数函数是需要返回值的 但是返回值他需要return 来做返回 ;具体就是

return (z);

return后面的括号可以不要; 比如return 0;还有就是 z可以是表达式 或者其他形式;这样的化还是带上()比较好;函数返回值的类型是你事先定义好的; 必须最后输出的结果必须是这个类型的返回值;但是把在执行体中 的z ,他是不一定非要是这个类型的返回值 了也可以是其他的;这取决与上面对形参的定义 以及 执行体中的具体过程;那你坑定会问 如果不一样了怎么办;那么系统会强制转换就行;

7.3.5 对调用函数的声明

对调用函数的声明是必不可少的;用为编译的时候你遇到调用某个函数的时候;你会发现系统根本不知道那个函数是什么;这就会出现error 所以要事先声明;那么既然声明那么就是让电脑知道你;所以声明要包括 类型名 函数名 参数类型 参数名 参数的数量 ;都要清楚 ;就相当于一个没有执行体的定义过程;当然这里面函数名字可以省略;
函数类型 函数名(参数类型1 参数名1,函数类型2 参数名2…);
函数类型 函数名(参数类型1 ,函数类型2 …);

就这两种;;

7.3.6 函数的嵌套调用;

之前说了 ,函数是可以嵌套调用的; 但是不能够相互间嵌套定义;
怎么嵌套调用 看一个实际例子就可以了;

#include <stdio.h>
int main (){
	int max4(int a,int b,int c,int d);
	int a,b,c,d,max;
	printf("please enter 4 number:");
	scanf("%d %d %d %d",&a,&b,&c,&d);
	max=max4(a,b,c,d);
	printf("max=%d\n",max);
	return 0;
}
int max4(int a,int b,int c,int d)
{
	int max2(int a,int b);
	return (max2(max2(max2(a,b),c),d));
}

int max2(int a,int b){
	return (a>=b?a:b);
}
/*
int max4(int a,int b,int c,int d)
{
	int max2(int a,int b);
	int m;
	m=max2(a,b);
	m=max2(m,c);
	m=max2(m,d);
	return (m);
}

int max2(int a,int b){
	return (a>=b?a:b);
}
*/

从这段代码可以看出;函数的嵌套;就是像上面这样使用的;多用就完了

7.3.7递归调用以及例题论证

函数的递归调用;咱们首先来看看咱们书本上面的题目;**直接调用或者间接调用函数本身叫做递归调用;**咱们看看 ;具体的例子;

/*这是一道求n!的题目*/
#include <stdio.>
int main(){
	int fac(int n)int n;
	int y;
	printf("input an inputer mnumber:");
	scanf("%d",&n);
	y=fac(n) ;
	printf("%d!=%d\n",n,y);
	return 0;
} 
int fac(int n){
	int f;
	if(n<0) printf("数据错误;")else if(n==o||n==1) f=1;
	else f=fac(n-1)*n;
	return(f);
}
#include <stdio.h>
int main(){
    int age(int n);
    printf("no.5.age:%d\n",age(5));
    reyurn(0);
}
int age(int n){
    int c;
    if(n==1) c=10;
    else c=age(n-1)+2;
    return (c);    
}

就是这两道题目;然后咱们看看就是;什么是函数的·递归调用就是 说 咱们 就是首先函数当调用自己的 那么就是会出现这个情况 ;执行这个函数 ;遇到调用;原来是自己;调用自己;又遇到自己;在调用自己;如此反复;重复就是;出现了一个又一个的死循环;那么需要有一个能结束死循环的东西;所以就出现了 ;**if else 语句 来终止循环 ;**还有就是函数递归调用其实本质就是函数的回溯 递推 的过程;所谓回溯就是反推 一直推到终止条件;即使解决死循环的key ;有了这个Key 你就可返回来一步步求出具体值;那这个过程就是递推;;以上你可以看出;

求n!
先是age(n-1)*n
然后要求age(n-1)=age(n-2)*(n-1) 
.........
回溯到了 age(1)=1;
然后一步步倒回去;
得出age(2)
......
age(n-1)
age(n);
就是结束;

自己调用自己 就是函数递归调用;解决办法就是函数回溯 递推;

再看一道例题
??例三

#include <stdio.h>
int main(){
	void hanoi(int n,char one,char two,char three);
	int m;
	printf("input the number of diskes:");
	scanf("%d",&m);
	printf("the step to move %d diskes :\n",m);
	hanoi(m,'A','B','C');
}
void hanoi (int n,char one,char two,char three){
	void move(char x,char y);
	if(n==1) move(one,three);
	else{
		hanoi(n-1,one,three,two);
		move(one,three);
		hanoi(n-1,two,one,three);
	}
}
void move(char x,char y){
	printf("%c--->%c\n",x,y);
}

7.4 数组类做函数参数

7.4.1 数组元素作为函数实参

咱们看形参只是一个临时变量;他是在函数调用完后自行解除的;所以只能是实参来提供,而不能直接赋值;函数是可能被调用多次的;所以说只能形式参数可以是任意变量提供的数值;所以就是所以不能直接赋值实参,那么他就没有绝对的自由;只能是某一个储存空间数值的改变;如果是这样他是不合规矩的;他应该是任意储存空间都能提供数值;那么就是说;只能通过实参转化形参;
那那些可以作为实参呢;常量;变量;??表达式;数组;数组元素;…都可以做函数的实参;
那么我是不是可以理解为实参本身提供一个数据空间;那么常量;表达式;作为熟知的结果储存;实参的储存空间是一直不变的;而形式参数是用完就取消的;他是通过实参传递的;
这里我们讲一讲 数组元素 作为实参函数

/*输入10个数,要求输出其中最大的数以及这是第几个数字*/
#include <stdio.h>
int main(){
	int max(int x,int y);
	int a[10],m,n,i;
	printf("enter 10 integer numbers:");
	for(i=0;i<10;i++)
	scanf("%d",&a[i]);
	printf("\n");
	for(i=1,m=a[0],n=0;i<10;i++){
		if (max(m,a[i])>m){
			m=max(m,a[i]);
			n=i; 
		}
	}
	printf("the largest number is %d\nit is the %dth number.\n",m,n+1);
} 
int max(int x,int y){
	return (x>y?x:y);
}

从这串代码中我们可以看出数组元素就是某个数值;所以不用担心;它就是数值的赋值;

7.4.2 数组名 作函数参数(实参 形参)

先观察这道题目,仔细看看实参和形参位置的区别;

/*一维数组score 内放置十个成绩 求平均成绩*/
#include <stdio.h>
int main()
{
	float average(float array[10]);
	float score[10],aver;
	int i;
	printf("input 10 scores:\n");
	for (i=0;i<10;i++) scanf("%f",&score[i]);
	printf("\n");
	aver=average(score);
	printf("average score is %5.2f\n",aver);
	return 0;
	 } 
	 
	 float average(float array[10])
	 {int j;
	 float ever,sum=array[0];
	 for(j=1;j<10;j++) sum=sum+array[j];
	 ever=sum/10;
	 return(ever);
	 }

你会发现实参和形参都是数组;而实参那儿是数组名字;仔细研究是你会发现主函数定义的数组名字不一样;但是类型一样的;我来解释一下,这是为什么;
就是首先;;之前说了实参和形参的关系 ;就是函数实参是有一个单独的储存空间的,是为了给形参赋值而存在的;所以实参里面是一个空间;;因为我只要满足让形参知道这个数组的开头就行;所以实参是数组名字;就是给形参一个地址,储存的开头的地址;所以不需要是score[10],这样首先变成了第十一个元素的地址;不行;;反正不能出现[];;只要函数名字就行;只要地址就ok;而形参,他是需要定义一个全新的数组 ;应为他和原数组就是两个;但是他们的储存空间是同一个;也就是说他和变量还是不一样的;变量的形参是给予一个临时的储存空间;他是实参把地址给过来,他们是一个地址,自然是同一个储存空间;同理,形参数值改变,原本数组的数值也改变;
所以就是说类型就要给到;而且类型最好相同;但是来说数组数量可以有 可以没有;因为编译系统不检查形参的数组大小的;可以想象成要多大有多大;任意的;不限定大小;应为会有不同数组进来嘛;所以就是说只是实参吧第一位给了形参第一位 这样形参和原数组第一位读取后是等价的;第n位置也是相互等价的;
总而言之怎么用呢;
实参数组名就行;
形参类型名要定义;目前类型要一样(也会出现不一样的也许还没遇到);
形参的数组名不能和原数组一样;否则出现错误;
后面[]要有;可以不给数值;最好不给;、
咱们再看看一个例子

/*两个班级 35人 30人 调用average 分别求平均成绩*/
#include <stdio.h>
int main()
{
   float average(float array[],int q);
   float score1[3]={1};
   float score2[5]={1};
   float aver;
   int i;
   printf("input 10 scores:\n");
   for (i=0;i<3;i++) scanf("%f",&score1[i]);
   printf("\n");
   aver=average(score1,3);
   printf("average score is %5.2f\n",aver);
   
   
   	for (i=0;i<5;i++) scanf("%f",&score2[i]);
   printf("\n");
   aver=average(score2,5);
   printf("average score is %5.2f\n",aver);
   
   
   return 0;
    } 
    
    float average(float array[],int q)
    {int j;
    float ever,sum=array[0];
    for(j=1;j<q;j++) sum=sum+array[j];
    ever=sum/q;
    return(ever);
    }
    

再看一个例子:

/*用选择法对数组中十个数由小到大排列*/ 
#include <stdio.h>
int main()
{void sort(int array[],int n);
int a[10],i;
printf("enter array:\n");
for(i=0;i<10;i++) scanf("%d",&sa[i]);
sort(a,10);
printf("the sorted array:\n");
for(i=0;i<10;i++) 
{printf("%d",a[1]);
printf("\n");
}
return 0; 
}

void sort(int array[],int n)
{int i,j,k,t;
for(i=0;i<n-1;i++) 
{k=i;
for(j=i+1;j<n;j++)
if (array[j]<array[k]) 
k=j;
t=array[k];array[k]=array[i];array[i]=t;
}
}

看这个例子是选择法来对数组中是个数进行排列;咱们看一看 这里面定义的函数有依旧符和上面的逻辑;这里面是形参的数组在改变数值;但是由于他们是属于一个地址 ,所以说形参的数值改变;依旧会导致原数组储存数值的改变;然后输出原数组时候是改变后的值;

7.4.3 多维数组名做函数参数

多维数组名字也可以做函数参数;我们这儿就用二维数组来说;对于二维数组 int a[10][9];在做函数的参数时候,咱们实参依旧是函数名字即可;函数的形参有一点区别;既然是二维数组;那么就会有行列之分;然后就是计算机的数据必须要依照行数进行线性排列储存的;所以列数必须确定;行数可以省略;为什么,我们具体举个例子;比如我们不知道列数的情况下,咱们储存数据,是储存一行呢 ,还是两行呢;因为你不知道列数有多大,可以是一个,也可以是无穷无尽;所以说列数必须确定,这样电脑就按照行数线性排列了;总而言之,对于二维数组在形式参数里面可以写成以下几种形式;

int a[10][10]int a[][10];

咱们具体看一个例子;

/*有一个3*4的矩阵,求所有元素的最大值*/
 #include <stdio.h>
 int main()
 {int max_value(int array[][4]);
 int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};
 printf("max is %d:",max_value(a));
 return 0;
  } 
  
  int max_value(int array[][4])
  {int i,j,max;
  max=array[0][0];
  for(i=0;i<3;i++)
  for(j=0;j<4;j++)
  if(array[i][j]>max)max=array[i][j];
  return(max);
  } 

7.5 局部变量 与 全局变量

作用域 ;每个变量都有它的作用域;作用域可以局部使用,也可以在全局中声明;
其具体作用域的不同是看如何定义的;大致上咱们可以把变量有三种定义的方式;
1.在函数的开头定义;
2.在函数内的复合语句中定义;
3.在函数的外部定义;

根据定义不同,作用域不同,可以分为全局变量和局部变量;前两种定义的方式就是属于局部变量,后一种属于全局变量;

7.5.1 局部变量

在某一局部范围内有效,叫做局部变量;比如在函数某一个函数范围里面定义了函数,那么这个变量只在函数作用范围里面有效;
举例子:

float f1(int a)
{int b,c;
...
}
/*可以看出a,b,c只在f1这个函数内有效*/
char f2(int x,int y)
{int i,j;
...    
}
/*x,y,i,j在f2种有效*/
int main()
{int m,n
...
return 0;
}
/*只在主函数内有效*/

不同函数中可以使用同名的变量;但是这两个变量是属于不同函数的;就是两个东西;
咱们再看看在复合语句中,在某个函数内定义某个复合语句,在这个复合语句中定义某个变量他只在该复合语句中起到作用;就是说作用域只是该复合语句;
在这里插入图片描述他说标红那条里面的max它无法识别;说明前面在复合语句中定义的max在函数中无效;

7.5.2 全局变量

全局变量作用域就是从此处定义开始到程序结尾;
咱们看一看全局变量;刚才说的在外部定义属于全局声明;那么在外部声明的变量,他又一个单独的储存空间,一直存在;整个变量在整个源程序中都起着作用;不管是主函数对他定义的数值改变,还是调用函数对他的数值改变,都后导致储存空间里面的数值的改变;定义全局变量的时候,其第一个字母的大小写顺序要写成大写;但这不是规定,只是这个行业的习惯;

/*一个数组放置10个元素,写一个函数,调用此函数能求出这个数组的平均分,最高分,最低分*/ 
#include <stdio.h>
float Max=0,Min=0;
int main()
{float average(float array[],int n);
float ave,score[10];
int i;
printf("please enter 10 number:");
for(i=0;i<=9;i++)scanf("%f",&score[i]);
 ave=average(score,10);
 printf("max=%6.2f\nmin=%6.2f\naverage=%6.2f\n",max,min,ave);
 return 0;
}
float average(float array[],int n)
{int i;
float aver,sum=array[0];
Max=Min=array[0];
for(i=1;i<n;i++)
{if(array[i]>Max) Max=array[i];
else if(array[i]<Min) Min=array[i];
sum=sum+array[i];
 } 
 aver=sum/n;
 return (aver);
}

你仔细看这串代码 ,我们里面 Max ,Min 都是全局变量;其中的储存空间,以及储存空间的数值;不管是主函数还是调用函数都可以使用;你看 ,主函数里面可以输出Max和Min的数值,在调用函数里面可以储存数值进入全局变量之中;所以全局变量有它的优点;可以方便解决一些问题;
但是,在行业中,尽量还是少使用全局变量;因为工程里面经常多个源文件一起组合,如果全局变量多了导致连接时候出现很多错误;所以对于某个源文件里面的函数,尽量不要去有全局变量,让他变成一个封闭函数;这样增加了代码的可用性;大大方便了人们的工作;

全局变量尽量不要和局部变量重复;不然理解起来很麻烦;如下

#include <stdio.h>
int a=3,b=5;
int main()
{int max(int a,int b);
int a=8;
printf("max=%d\n",max(a,b));
return 0;
}

int max(int a,int b)
{int c;
c=a>b?a:b;
return (c);
}

咱们分析一下;就是函数首先定义俩个储存空间叫做a b,结果在main 函数中有定义了一个全新的储存空间也叫做a ;那么对于函数我怎么用呢;根据结果分析,咱们知道电脑采用的时就近原则,什么意思;就是同时a,明明应该报错;但是电脑却选择去执行;说明一点,电脑找到了一个储存空间;那就是后来定义的;怎么说就这样我函数体里面的a离我调用的近一点,所以我就用了它;同理咱们再往下看;在定义函数时候;咱们又有两个临时的储存空间a b ;和全局变量 a b ;又冲突了;那么我选择离我近一点的去使用;结果果然输出的是8;

7.6 变量的储存方式

7.6.1变量的储存方式

变量 我们从作用空间的范围去定义;可以分为全局变量;还有就是局部变量;
如果我们从时间的角度去定义;就是变量的生存期;又可以分为若干种变量;
1.自动变量
2.静态变量
3.寄存器变量
4.外部变量

既然从生存期来看,有的变量储存就消失了;有的一直储存;前者是分配临时储存空间,然后用完之后就消散了;所以因此变量就出现了两种储存方式就是 静态储存方式 和 动态储存方式;其储存的地方就是一下几个地方;
1.程序区 (符号常量)
2.静态储存区(全局变量):一般都是程序执行开始出来储存单元;程序结束时储存单元释放;
3.动态储存区(形参,没有用关键字static申明的变量,函数调用时现场保护和返回地址):函数调用开始时储存,结束时自行解散;

这种动态储存空间分配是动态的;随机分配;两次调用同一个函数他们的给定地址都不一定一样;

所以我们总结出定义一个变量是需要从空间时间 两个方面去定义他;空间就是定义数据类型;及变量的大小与储存方式;时间就是定义数据的储存类别,就是放在那个区了,就是本质定义他是变的还是不变的;


通过结合后面的堆和栈。
程序区;
静态储存区 :等待程序结束后解散。
动态储存区(栈);对于调用函数,调用时开始 ,调用结束后自行解散
自由储存区(堆);==比栈还自由,想开辟开辟,想结束结束。


一,auto变量 自动变量
咱们形式参数,还有那个函数里面定义的常用那些变量 ,其实都属于自动变量;他们都是在函数开始时候临时出现一个储存空间,然后在函数结束时自行解散;

#include <stdio.h>
int a=3,b=5;
int main()
{int max(int a,int b);
int a=8;
printf("max=%d\n",max(a,b));
return 0;
}

int max(int a,int b)
{int c;
c=a>b?a:b;
return (c);
}

你看里面的int a;int c;都是一个动态储存;动态储存一般要在前面加auto,来表示动态变量;但是一般可以省略;所以我们一般就定义什么 int a;float b;不写auto自动隐含表示“自动储存类别”;

二,静态局部变量(static局部变量)
如果咱们想让一个储存单元不是动态储存的,咱们可以就是使它不变嘛;在前面加上static,那么这个变量的储存单元会一直延续到程序结束;

#include <stdio.h>
int main()
{int f(int);
int a=2,i;
for(i=0;i<3;i++)printf("%d\n",f(a));
return 0;	
 } 
 int f(int a)
 {auto int b=0;调用时赋初值,因为每次调用时会重新分配空间,所以每次数值都不确定
 所以电脑不会赋初值;每次初值内容不可知;
 static int c=3;编译时赋初值;因为每次调用时不会重新分配空间,所以每次
 数值都确定所以电脑会赋初值;初值0或‘\0’
 b=b+1;
 c=c+1;
 return(a+b+c);
 }

这个c变成静态变量之后,储存空间就算函数结束也不消散;这里面当第二次执行到 static int c=3;时候这是已经定义过的 那么这句话在第二次就会变成废话;不起到任何作用;
总而言之:
1.定义在静态储存区;
2.编译时赋初值;编译只有一次;所以当再次调用不会赋初值;
3.如果没赋初值那么系统会附上初值0或\0;
4.仍然是局部变量,作用域没变;
5.长期占内存;可读性不高;、
因为他的值不变 ,就会一直占用内存;而且在调用到达次数之后,你很难知道他的储存单元数值是多少;可读性降低;

/*输出1到5 各自的阶乘数值*/
#include <stdio.h>
int main()
{int f(int);
int i;
for(i=1;i<=5;i++)printf("%d!=%d\n",i,f(i));
return 0;	
 } 
 int f(int a)
 {
 static int c=1;
 
 c=c*a;
 return(c);
 }

三,寄存器变量
一般而言,内存 运算器 变量 控制器 ;控制器发出指令调用变量从内存到运算器中;储存相反;
什么是寄存器变量,就是对于一些某些变量的值它调用的次数足够多;这样每次从内存中调用就很慢;这样我们就把数值放置在寄存器种 ,这样就会大大减少了处理时间;

register int f;

以上所说的auto static register 都是对储存类型的定义;然后就是不能省略数据类型的定义;
extern 只是声明而已;不属于以上三种;它可以省int ;

7.6 变量的储存方式和生存期

7.6.1动态储存方式和静态储存方式

变量 我们从作用空间的范围去定义;可以分为全局变量;还有就是局部变量;
如果我们从时间的角度去定义;就是变量的生存期;又可以分为若干种变量;
1.自动变量
2.静态变量
3.寄存器变量
4.外部变量

既然从生存期来看,有的变量储存就消失了;有的一直储存;前者是分配临时储存空间,然后用完之后就消散了;所以因此变量就出现了两种储存方式就是 静态储存方式 和 动态储存方式;其储存的地方就是一下几个地方;
1.程序区 (符号常量)
2.静态储存区(全局变量):一般都是程序执行开始出来储存单元;程序结束时储存单元释放;
3.动态储存区(形参,没有用关键字static申明的变量,函数调用时现场保护和返回地址):函数调用开始时储存,结束时自行解散;

这种动态储存空间分配是动态的;随机分配;两次调用同一个函数他们的给定地址都不一定一样;

所以我们总结出定义一个变量是需要从空间时间 两个方面去定义他;空间就是定义数据类型;及变量的大小与储存方式;时间就是定义数据的储存类别,就是放在那个区了,就是本质定义他是变的还是不变的;

7.6.2 局部变量的储存类别

一,auto变量 自动变量
咱们形式参数,还有那个函数里面定义的常用那些变量 ,其实都属于自动变量;他们都是在函数开始时候临时出现一个储存空间,然后在函数结束时自行解散;

#include <stdio.h>
int a=3,b=5;
int main()
{int max(int a,int b);
int a=8;
printf("max=%d\n",max(a,b));
return 0;
}

int max(int a,int b)
{int c;
c=a>b?a:b;
return (c);
}

你看里面的int a;int c;都是一个动态储存;动态储存一般要在前面加auto,来表示动态变量;但是一般可以省略;所以我们一般就定义什么 int a;float b;不写auto自动隐含表示“自动储存类别”;

二,静态局部变量(static局部变量)
如果咱们想让一个储存单元不是动态储存的,咱们可以就是使它不变嘛;在前面加上static,那么这个变量的储存单元会一直延续到程序结束;

#include <stdio.h>
int main()
{int f(int);
int a=2,i;
for(i=0;i<3;i++)printf("%d\n",f(a));
return 0;	
 } 
 int f(int a)
 {auto int b=0;调用时赋初值,因为每次调用时会重新分配空间,所以每次数值都不确定
 所以电脑不会赋初值;每次初值内容不可知;
 static int c=3;编译时赋初值;因为每次调用时不会重新分配空间,所以每次
 数值都确定所以电脑会赋初值;初值0或‘\0’
 b=b+1;
 c=c+1;
 return(a+b+c);
 }

这个c变成静态变量之后,储存空间就算函数结束也不消散;这里面当第二次执行到 static int c=3;时候这是已经定义过的 那么这句话在第二次就会变成废话;不起到任何作用;
总而言之:
1.定义在静态储存区;
2.编译时赋初值;编译只有一次;所以当再次调用不会赋初值;
3.如果没赋初值那么系统会附上初值0或\0;
4.仍然是局部变量,作用域没变;
5.长期占内存;可读性不高;、
因为他的值不变 ,就会一直占用内存;而且在调用到达次数之后,你很难知道他的储存单元数值是多少;可读性降低;

/*输出1到5 各自的阶乘数值*/
#include <stdio.h>
int main()
{int f(int);
int i;
for(i=1;i<=5;i++)printf("%d!=%d\n",i,f(i));
return 0;	
 } 
 int f(int a)
 {
 static int c=1;
 
 c=c*a;
 return(c);
 }

三,寄存器变量
一般而言,内存 运算器 变量 控制器 ;控制器发出指令调用变量从内存到运算器中;储存相反;
什么是寄存器变量,就是对于一些某些变量的值它调用的次数足够多;这样每次从内存中调用就很慢;这样我们就把数值放置在寄存器种 ,这样就会大大减少了处理时间;

register int f;

以上所说的auto static register 都是对储存类型的定义;然后就是不能省略数据类型的定义;
extern 只是声明而已;不属于以上三种;它可以省int ;

7.6.3 全局变量的储存类别

咱们看看全局变量,全局变量是储存在静态储存区域的;在这个区域说明这个变量生存期在整个程序运行过程中都是行之有效的;但是他的作用域并非整个过程;是从定义位置起,到结束;但是如果咱们想扩展他的作用域怎么办;

一,在一个文件里面拓展外部变量的作用域;
这里我只能从一个源文件去理解;
用关键词extern ;定义你需要拓展的那一行;这样从此处开始到结束咱们就是新的作用域;

/*一个数组放置10个元素,写一个函数,调用此函数能求出这个数组的平均分,最高分,最低分*/ 
#include <stdio.h>

int main()
{float average(float array[],int n);
float ave,score[10];
int i;
extern float Max,Min; 
printf("please enter 10 number:");
for(i=0;i<=9;i++)scanf("%f",&score[i]);
 ave=average(score,10);
 printf("max=%f\nmin=%f\naverage=%f\n",Max,Min,ave);
 return 0;
}
float Max=0,Min=0;
float average(float array[],int n)
{int i;
float aver,sum=array[0];
Max=Min=array[0];
for(i=1;i<n;i++)
{if(array[i]>Max) Max=array[i];
else if(array[i]<Min)
 Min=array[i];
sum=sum+array[i];
 } 
 aver=sum/n;
 return (aver);
}

extern 不是定义这个变量;只是拿过来用 ,所以可以省略数据类型;
extern int a,b,c;
extern a,b,c;

二.将外部变量的作用域扩展到其他文件
我之前介绍了,在同一个源文件里面;使用extern 把变量的作用域往外拓展;那我不仅就问了,那我在不同源文件里面该怎么扩展其作用域,首先他既然是一个全局变量,然后就是他的储存空间是不释放的,所以说他的生存期随程序是一直存在的;所以其他源文件是可以使用它的;你要在其他源文件里面使用他,你作用域不够啊;就需要你拓展作用域;那我怎么拓展呢?就是你需要一个同样使用extern来进行声明;比如 extern Num ;这样在编译和连接时,系统就知道有外部连接;可以从别处找到已经定义的外部变量num;并且把外部变量的作用域拓展到此处;

##file.1
#include <stdio.h>
int A;
int main()
{int power(int);
int b=3,c,d,m;
printf("enter the number a and its power m:\n");
scanf("%d %d ",&A,&m);
c=A*b;
printf("%d*%d=%d\n",A,b,c);
d=power(m);
printf("%d**%d=%d\n",A,m,d);
return 0;	
} 
##file.2
extern A;
int power(int n)
{int i,y=1;
for(I=1;i<=n;i++) y*=A;
return y;
}

无论是在一个源文件还是其他源文件里面使用外部变量,都需要进行extern 的声明;具体计算机是如何处理的呢;首先当他识别到了extern 的时候 ,他会先在自己的本文件里面去寻找这个外部变量;然后如果找不到,他会去其他源文件里面去寻找;再找不到就报错了;

三,将外部变量的作用域限制在本文件中;
同样咱们使用外部变量在自己的源文件中,在其他源文件中;这是一种方法,但是会导致一些其他的麻烦;就是我一个外部变量是对整个程序都是可以使用的;这会导致很多程序都会对这个变量造成影响;万一出了错;怎么找到这个影响来自哪里;所以不建议把外部变量引用到其他源文件中;这样我需要做的是是这个外部变量,只对你正在编译得这个源文件有效,对其他源文件无效这样你会发现程序的可读性增加了;维护也变得容易了很多;

加上 static声明;
外部变量------>静态外部变量;只能作用于本源文件中;

同样static 当声明全局变量和局部变量时产生的效果是不一样的;
1.对于局部变量来说,当你使用static时候对变量产生的效果是,让动态储存空间,变为金泰储存空间 ,在作用域不变的情况下,我可以一直存在;
2.对于全局变量,我可以是这个原本可以对整个程序可以使用的环境改变为只能对该定义下的源程序作用;

7.7关于变量的声明与定义

7.8.1 声明 &&定义;
什么是声明 什么是定义;
它们是有区别的;
对于函数,声明部分和定义部分是区分开的;
对于变量;出现时无非就是两种 一种是需要储存空间的;另外一种是不需要储存空间的;前者叫定义性质声明;后者叫做引用性质的申明;这是最标准理解;
换一种子狭义理解;对变量;可以分为有储存空间叫做定义;不要储存空间叫做声明;
根据具体的效果来决定他是定义还是声明;
static 局部 叫做定义
static 全局 叫声明
内部函数用 static 声明
外部用 extern 声明
extern 全局 叫声明
int 定义
auto 定义

/*一个数组放置10个元素,写一个函数,调用此函数能求出这个数组的平均分,最高分,最低分*/ 
#include <stdio.h>

int main()
{float average(float array[],int n);
float ave,score[10];
int i;
extern float Max,Min; 这儿叫做外部变量的声明;同时可以引用多次;
printf("please enter 10 number:");
for(i=0;i<=9;i++)scanf("%f",&score[i]);
 ave=average(score,10);
 printf("max=%f\nmin=%f\naverage=%f\n",Max,Min,ave);
 return 0;
}
float Max=0,Min=0;这个是外部变量的定义;只能定义一次;
float average(float array[],int n)
{int i;
float aver,sum=array[0];
Max=Min=array[0];
for(i=1;i<n;i++)
{if(array[i]>Max) Max=array[i];
else if(array[i]<Min)
 Min=array[i];
sum=sum+array[i];
 } 
 aver=sum/n;
 return (aver);
}

7.8 内部函数&&外部函数

变量有局部与全局的概念;那么函数呢?
函数本质是全局的;但是也可以定义为局部,即只在本文件内使用;这样就会是不同人编译不同文件时候,你的文件各自的封闭性;不用担心重复,交叉;可读性 维护方便;外部呢,一般系统自动认为没有被定义为内部的都是外部函数;
内部函数用 static 声明 让函数只作用于本源文件中;也叫静态函数;
外部用 extern 声明;一般可以省略;函数本来就有全局性;本来就有extern ;这里面可以省略;所以对于我们之前学习的所有函数都是属于外部函数;

#file1
#include <stdio.h>
int main()
{extern void enter_string(char str[]);
extern void delete_string(char str[],char ch);
extern void print_string(char str[]);
char c,str[80];
enter_string(str);
scanf("%c",&c);
printf_string(str);
return 0;
}
#file2
void enter_string(char str[80])
{
	gets(str);
}
#file3
void delete_string(char str[],char ch)
{
	int i,j;
	for(i=j=0;str[i]!='\0';i++){
		if(str[i]!=ch) str[i++]=str[i];
		str[j]='\0';
	}
}
#file4
void print_string(char str[])
{
	printf("%s\n",str);
}

7.9 函数宏定义

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值