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

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

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;
}
/*只在主函数内有效*/


不同函数中可以使用同名的变量;但是这两个变量是属于不同函数的;就是两个东西;

咱们再看看在复合语句中,在某个函数内定义某个复合语句,在这个复合语句中定义某个变量他只在该复合语句中起到作用;就是说作用域只是该复合语句;

1670491141373.jpg

他说标红那条里面的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.7 变量的储存方式和生存期


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


变量 我们从作用空间的范围去定义;可以分为全局变量;还有就是局部变量;

如果我们从时间的角度去定义;就是变量的生存期;又可以分为若干种变量;

1.自动变量

2.静态变量

3.寄存器变量

4.外部变量

既然从生存期来看,有的变量储存就消失了;有的一直储存;前者是分配临时储存空间,然后用完之后就消散了;所以因此变量就出现了两种储存方式就是 静态储存方式 和 动态储存方式;其储存的地方就是一下几个地方;

1.程序区 (符号常量)

2.静态储存区(全局变量):一般都是程序执行开始出来储存单元;程序结束时储存单元释放;

3.动态储存区(形参,没有用关键字static申明的变量,函数调用时现场保护和返回地址):函数调用开始时储存,结束时自行解散;

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


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


7.7.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.7.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.8关于变量的声明与定义


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.9 内部函数&&外部函数


变量有局部与全局的概念;那么函数呢?

函数本质是全局的;但是也可以定义为局部,即只在本文件内使用;这样就会是不同人编译不同文件时候,你的文件各自的封闭性;不用担心重复,交叉;可读性 维护方便;外部呢,一般系统自动认为没有被定义为内部的都是外部函数;

内部函数用 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.10函数宏定义

1670491480905.jpg

1670491490231.jpg

1670491497043.jpg

1670491503680.jpg

1670491511871.jpg

1670491522088.jpg

相关文章
|
人工智能
AI 古籍修复的意义
AI 古籍修复的意义
398 0
|
芯片 算法 异构计算
如何打破边缘端芯片算力有限的困局?阿里 AILabs 这么做!
在自研硬件上,和芯片厂商深度合作针对中低端芯片做出了特例优化,落地了手势识别、宠物检测和笔尖检测等业务。
3740 0
|
12月前
|
缓存 NoSQL Java
java电商项目(十二)
本文接续前几个文章的项目进行讲解
192 1
|
消息中间件 SpringCloudAlibaba Java
第十章 SpringCloud Alibaba 之 Nacos discovery
第十章 SpringCloud Alibaba 之 Nacos discovery
660 1
|
12月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
654 4
|
12月前
|
网络安全 Windows
查看SSH配置文件
查看SSH配置文件
1355 1
|
12月前
|
存储 前端开发 搜索推荐
ClkLog基于ClickHouse 的百万日活实测报告
自 ClkLog 上线以来,我们不断吸纳用户需求,提升产品的支持能力。今年下半年,我们遇到了日活跃用户数达到百万级别的客户。为了给 ClkLog 用户提供可靠的技术建议和解决方案,同时也为了节省成本,在Clickhouse官方支持下,我们在阿里云上对 ClickHouse 社区版、企业版进行了详细测试和成本分析。
|
敏捷开发 测试技术 持续交付
软件开发过程中的最佳实践和代码质量评估
在软件开发过程中,采用最佳实践和评估代码质量对于确保软件的稳定性和可维护性至关重要。通过明确的需求、合理的开发流程、良好的代码规范以及严格的代码评估,我们可以降低软件开发过程中的风险,并提升开发效率和软件质量。
1529 2
|
网络虚拟化 网络架构
思科三层交换机配置步骤
思科三层交换机配置步骤
1118 6
思科三层交换机配置步骤
|
机器学习/深度学习 人工智能 自然语言处理
自然语言处理:实现智能问答系统的关键技术
自然语言处理在实现智能问答系统中起着重要作用。通过文本预处理、信息检索、语义理解和答案生成等关键技术,我们可以构建高效准确的智能问答系统,为用户提供便捷的信息获取方式。随着深度学习等技术的发展,智能问答系统的性能还将得到进一步提升,为人们提供更加智能化的服务。
1126 0