/*输入两个数,求较大的值。*/
#include<stdio.h>
void main()
{
int a,b;
int max(int a,int b);
scanf("%d%d",&a,&b);
printf("\n两者中较大的值为:%d\n",max(a,b));
}
int max(int a,int b)
{
return (a>b)?a:b;
}
- 函数声明中的参数名可以省写,如上例中的声明也可以写成
int max(int,int);
- 在调用函数的过程中调用另一个函数,称为函数的嵌套调用。
- 在调用一个函数过程中直接或间接调用本函数,称为函数的递归调用。
函数的调用过程
- 在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数max中的形参被分配到内存单元中。
- 将实参对应的值传递给形参。
- 在执行max函数期间,由于形参已经有值,就可以进行有关的运算(如把a和b比较)
- 通过return语句将函数的值带回到主调函数。
- 调用结束,形参单元被释放。注意,实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,但不会改变主调函数的实参的值。
注意:在C语言中,实参向形参的数据传递是“值传递”,单向传递,只由实参传给形参,而不能由形参传给实参。
数组名作函数参数
/*有10个学生成绩,用一个函数求平均成绩。*/
#include<stdio.h>
void main()
{
float average(float); /*声明的形参可以省略*/
float score[10];
for(int i=0;i<10;i++)
scanf("%f",&score[i]);
printf("平均值是:%4.2f\n",average(score));
}
float average(float array[10])
{
float sum = 0; /*必须给初始值*/
for(int i=0;i<10;i++)
sum+=array[i];
return sum/10;
}
- 由于数组名代表数组的首地址,只是将数组的首元素的地址传递给所对应的形参,对应的形参应当是数组名或指针变量。
- 用数组作为函数参数,在调用函数时并不另外开辟一个存放形参数组的空间,这点是和用变量作函数参数是不同的。
- 数组名代表数组的首元素的地址,因此,用数组名作函数实参时,只是将实参数组的首元素的地址传给形参数组,形参数组名获得了实参数组的首元素的地址。因此array[0]和score[0]具有同一地址,共占同一存储单元,具有相同的值。
- 在程序中,定义函数average时声明了形参数组array的大小为10,但在实际应用中,指定其大小并不起任何中,因为C语言编译对形参数组大小不做检查,所以形参数组可以不指定大小,在定义数组时,可以在数组名后面跟一个空的方括号,它们效果是相同的。
- 形参数组名实际上是一个指针变量名。
- 形参数组中各元素的值如发生变化会使实参数组元素的值同时发生变化,这一点是与变脸作函数参数的情况不相同,务请注意。
/*输入10个数,选择排序*/
#include<stdio.h>
void main()
{
void sort(int array[],int); /*有两个参数时,形参不能省*/
int array[10],n=10,i;
for(i=0;i<n;i++)
scanf("%d",&array[i]);
sort(array,n);
for(i=0;i<n;i++)
printf("%d ",array[i]);
printf("\n");
}
void sort(int array[],int n)
{
int x;
for(int i=0;i<n;i++)
{
for(int j=i+1;j<10;j++)
{
if(array[i]>array[j])
{
x = array[i];
array[i] = array[j];
array[j] = x;
}
}
}
}
用变量名或数组元素作函数参数时,传递的是变量的值,用数组名作函数参数时,传递的是数组首元素的地址。可知,调用函数时的虚实结合的方式有两类:一类是值传递方式,一类是地址传递方式。
值传递方式时,系统为形参另开辟存储单元,实参与形参不是同一单元,因此形参的值改变不会导致实参值得改变,值传递是单向的,只能从实参传到形参,而不能由形参传到实参。
地址传递方式时,传递的是地址,系统不会为形参数组另外开辟一段内存单元来存放数组的值,而是使形参数组与实参数组共占同一段内存单元。由于这个特点,可以利用在函数中改变形参数组的值来改变实参数组的值。从现象上看,好似传递是双向的,从实参传递到形参,又从形参传递到实参。但是从严格意义上说,传递仍然是单向的,仅仅传递的是地址而已。由于地址共享,才会出现改变形参数组的值也改变实参数组的值的现象。这是一个可以利用的重要技巧。
- 二维数组是由若干个一维数组组成的,在内存中,数组是按行存放的,因此,在定义二维数组时,必须指定列数(即一行中包含几个元素)。
float highest_score(float array[][5]); /*正确*/
float highest_score(float array[][]); /*错误*/
float highest_score(float array[4][]); /*错误*/
变量的作用域和生存期
- 在同一个源文件中,外部变量和局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用,此时局部变量是有效的。
设置全局变量的作用是增加函数间数据联系的渠道。由于同一源程序文件的所有函数都能引用全局变量的值,因此如果在一个函数中改变了全局变量的值,就能影响到其他函数,相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加函数间的联系渠道,在调用函数时有意改变某个全局变量的值,这样,当函数执行结束后,不仅能得到一个函数返回值,还能使全局变量获得一个新值,从效果上看,相当于通过函数调用能得到一个以上的值。
虽然全局变量有以上优点,但建议不必要时不要使用全局变量,因为:
- 1、全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。
- 2、它使函数的通用性降低了,因为函数在执行时要依赖于其所在的程序文件中定义的外部变量。如果将一个函数移到另一个文件中,还要讲有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现冲突,降低程序的可靠性和通用性。、
- 在程序设计中,在划分模块时要求模块的“内聚性”强、与其他模块的“耦合性”弱,即模块的功能要单一(不要把许多互不相干的功能放到一个模块中),与其他模块的相互影响要尽量少,而用全局变量是不符合这个原则的。
- 一般要求把C程序中的函数做成一个封闭体,除了可以通过“实参-形参”的渠道与外界发生联系外,没有其他渠道。这样的程序移植性好,可读性强。
- 使用全局变量过多,会减低程序的清晰性,人们旺旺难以清楚地判断出瞬时各个外部变量的值,在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。
变量的存储方式和生存期
auto-自动变量
- 函数的形参
- 在函数中定义的变量(包括在复合语句中定义的变量)
static-静态变量
/*输出1~5的阶乘值*/
#include<stdio.h>
void main()
{
int fac(int);
for(int i=1;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
}
int fac(int n)
{
static int f=1;
f = f*n;
return f;
}
运行结果:
1!=1
2!=2
3!=6
4!=24
5!=120
- 静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放。
- 而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。
- 对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。
- 而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
- 如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)和空字符(对字符变量)。
- 而对自动变量来说,如果不赋初值则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的值是不可知的。
- 虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的,因为它是局部变量,只能被本函数引用,而不能被其他函数引用。
- 用静态存储会多占内存(长期占用不释放,而不能像动态存储那样,一个存储单元可供多个变量使用,节约内存),降低程序的可读性,当调用次数多时往往弄不清静态局部变量的当前值是什么。因此,若非必要,不要多动静态局部变量。
有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。这种加上static声明,只能用于本文件的外部变量,称为静态外部变量。
- static对局部变量和全局变量的作用不同。
- 对局部变量来说,它使变量由动态存储方式改变为静态存储方式。
- 而对全局变量来说,它使变量局部化(局部于本文件),但仍未静态存储方式。
register-寄存器变量
由于现在计算机的运行速度愈来愈快,性能愈来愈好,优化的编译系统能够识别使用频繁的变量,从而自动将这些变量存在寄存器中,而不需要程序设计者指定。
extern-外部变量的作用范围
在任一个文件中定义外部变量Num,而在另一文件中用extern对Num作“外部变量声明”,即“extern Num;”。在编译和连接时,系统会由此知道Num是一个已在别处定义的外部变量,并将在另一文件中定义的外部变量的作用域扩展到本文件,在本文件中可以合法地引用外部变量Num。
变量存储类别 | 函数内 | 函数内 | 函数外 | 函数外 |
---|---|---|---|---|
- | 作用域 | 存在性 | 作用域 | 存在性 |
自动变量和寄存器变量 | √ | √ | × | × |
静态局部变量 | √ | √ | × | √ |
静态外部变量 | √ | √ | √(只限本文件) | √ |
外部变量 | √ | √ | √ | √ |
内部函数和外部函数
C语言存储类型及各存储类型作用域和生存域比较 - Banana_201 - 博客园
表达式:
[]
.