上文已经说了什么是函数、函数的定义以及函数的分类等内容,接下来要说的是关于函数需要注意的问题以及变量的作用域和存储方式,中间还会举一个例子相对详细的说明应用函数对程序的好处。
注意的问题
函数调用和函数定义的顺序
如果函数调用写在了函数定义前面,则必须加函数前置声明
函数前置声明:
1、告诉编译器即将可能出现的若干个字母代表的是一个函数
2、告诉编译器即将可能出现的若干个字母所代表的函数的形参和返回值的具体情况
3、函数声明是一个语句,末尾必须加分号
4、对库函数的声明是通过 # include <库函数所在的文件的名字.h>来实现的
当主调函数(主函数或其他函数)调用另一个函数时,该被调函数需要放在主调函数的前边(除非已经进行了前置声明),否则编译器会报错,下面举一个例子,
例1:
# include <stdio.h>
int main(void)
{
f();
return 0;
}
void f(void)
{
printf("又开始下雪了!\n");
}
编译器会报出这样的错误
h.cpp(5) : error C2065: 'f' : undeclared identifier
h.cpp(11) : error C2373: 'f' : redefinition; different type modifiers
大致意思是:无法识别main()中的f(), 重新定义了不同类型的修饰语
因为程序先从主函数(在本程序中,主函数就是主调函数)开始执行,执行到f() 时,由于f() 这个函数的定义在主函数后面,所以编译器因无法识别而报错,要想改正这个错误,我们可以把f() 函数的定义放在主调函数的前边,即:
# include <stdio.h>
void f(void)
{
printf("又开始下雪了!\n");
}
int main(void)
{
f();
return 0;
}
如果不想这样做,那我们就要在主调函数前加一个f() 的声明,这样错误也能得到改正,代码如下:
# include <stdio.h>
void f(void); //函数声明,分号不能丢掉!!!
int main(void)
{
f();
return 0;
}
void f(void)
{
printf("又开始下雪了!\n");
}
需要注意的是,函数声明后面的分号绝对不能丢掉
最终输出结果为:
再有一个例子,
例2:
# include <stdio.h>
void f(void); // 第三行 使用前要先声明,像左边这样!!!
void g(void)
{
f();
}
void f(void)
{
printf("又开始下雪了!\n");
}
int main(void)
{
g();
return 0;
}
最终输出结果为:
主函数先调用g() 函数,然后g() 函数调用f() 函数,因为g() 函数定义在f() 函数定义之前,所以要想调用它,就得加一个前置声明(第3行)
强调一下,对于printf 和scanf 等库函数的声明是通过 # include <库函数所在的文件的名字.h>来实现的
什么是形式参数
形式参数是出现在函数定义中的变量,只能在其被定义的函数中使用,简称形参
什么是实际参数
实际参数是出现在主调函数中的变量,离开主调函数进入被调函数时也无法使用,简称实参
注意:形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量
形参和实参的关系
1、个数相同
2、位置一一对应
3、数据类型必须相互兼容
如何理解这三句话,下面举一个例子
例3:
# include <stdio.h>
void f(int i, float j)
{
printf("%d %f\n", i, j);
}
int main(void)
{
f(5, 6.6);
return 0;
}
在这个程序中,i 和j是形式参数,5 和6.6 是实际参数,它们都是两个,这样没有错误,如果再添加或减少一个形参或实参,编译器就会报错,所以说形参和实参个数必须相等;然后,他们的位置必须一一对应,如上所示,如果不这样,比如说把6.6放在函数下边,那也是不对的;对于第三条,如果i的类型是float类型,程序也能编译通过,但可能会出现数据存储出错现象,如果i 的类型是char 类型,那输入5 就肯定不行了,当然,最好还是让它们的类型一样。
关于如何利用函数使代码更精炼,下面会就如何判断一个数是否为素数举例,
例4:
如何判断一个数是不是素数,用我们以前(学函数以前)的方法,我们可以这样写代码:
例4.1
# include <stdio.h>
int main(void)
{
int val;
int i;
scanf("%d", &val);
for (i=2; i<val; ++i)
{
if (0 == val%i)
break;
}
if (i == val)
printf("该数是素数!\n");
else
printf("该数不是素数!\n");
return 0;
}
这个程序的主要部分是for循环和if语句,当你输入一个数字时,他会判断该数字是否是素数,是则输出“该数是素数!”,否则输出“该数不是素数!”,这样写虽然实现了我们想要的功能,但这样写不好,代码的可重性不强(即利用率不高),我们可以用单独的函数来实现这个功能,然后再对这个函数进行调用,代码如下:
例4.2
# include <stdio.h>
bool IsPrime(int val)
{
int i;
for (i=2; i<val; ++i)
{
if (0 == val%i)
break;
}
if (i == val)
return true;
else
return false;
}
int main(void)
{
int val;
int i;
scanf("%d", &val);
if ( IsPrime(val) )
printf("该数是素数!\n");
else
printf("该数不是素数!\n");
return 0;
}
这里我们用到了一个布尔型变量bool,它是逻辑型变量的定义符,它的值只有true 和false ,至于为什么定义的这个函数名字叫IsPrime而不是其他字符或字符串,在上文中已经说过,这里不再重复。这样写的代码可重性提高,减少了计算机执行的语句量。
再举一个例子, 求1到某个数字(包括该数字)之间所有的素数,并将其输出
我们可以这样写代码:
例4.3
# include <stdio.h>
int main(void)
{
int val;
int i;
int j;
scanf("%d", &val);
for (i=2; i<=val; ++i)
{
//判断i是否是素数,是输出,不是不输出
for (j=2; j<i; ++j)
{
if (0 == i%j)
break;
}
if (j == i)
printf("%d\n", i);
}
return 0;
}
如我们输入13,结果会输出2,3,5,7,11,13,这样写不好,不仅代码的可重性不高,而且也不容易让人理解,我们可以用一个函数来判断一个数是否是素数,如下所示:
例4.4
# include <stdio.h>
bool IsPrime(int m)
{
int i;
for (i=2; i<m; ++i)
{
if (0 == m%i)
break;
}
if (i == m)
return true;
else
return false;
}
int main(void)
{
int val;
int i;
scanf("%d", &val);
for (i=2; i<=val; ++i)
{
if ( IsPrime(i) )
printf("%d\n", i);
}
return 0;
}
这样写有优点也有缺点
优点:
代码比 上一个程序 更容易理解
代码的可重用性比 上一个 高
缺点:
可重用性仍然不是非常高,
比如求1000个数字,它们每个数字从1到它们本身的素数
则
for (i=2; i<=val; ++i)
{
if ( IsPrime(i) )
printf("%d\n", i);
}
要被计算机执行1000次
所以,我们还可以再添加一个函数,使代码更加精炼,代码如下:
例4.5
# include <stdio.h>
//本函数的功能是:判断m是否是素数,是返回true,不是返回false
bool IsPrime(int m)
{
int i;
for (i=2; i<m; ++i)
{
if (0 == m%i)
break;
}
if (i == m)
return true;
else
return false;
}
//本函数的功能是把1到n之间所有的素数在显示器上输出
void TraverseVal(int n)
{
int i;
for (i=2; i<=n; ++i)
{
if ( IsPrime(i) )
printf("%d\n", i);
}
}
int main(void)
{
int val;
scanf("%d", &val);
TraverseVal(val);
return 0;
}
本程序和 例4.4 相比较
代码量更少 可重用性更高
下面说一下变量的作用域和存储方式
变量的作用域和存储方式:
按作用域分:
全局变量
在所有函数外部定义的变量都属于全局变量
全局变量使用范围:从定义到整个程序结束
局部变量
在一个函数内部定义的变量或者函数的形参 统称为局部变量
void f(int i)
{
int j = 20;
}
i和j都属于局部变量
局部变量使用范围:只能在本函数内部使用
例5.1:
# include <stdio.h>
int k = 1000;
void f(int i)
{
int j = 20;
printf("k = %d\n", k);
}
//第10行
int main(void)
{
int i = 10;
f(88);
return 0;
}
在这个程序中,k是全局变量,但要注意因为k在f 函数中被使用,所以当k的定义在f函数后边(即如果把int k = 1000放在第10行),编译器会报错。i和j都是局部变量,只能在被定义的函数中使用
注意的问题:
全局变量和局部变量命名冲突的问题
在一个函数内部如果定义的局部变量的名字和全局变量的名字一样时,局部变量会屏蔽掉全局变量!!!
示例如下
例5.2:
# include <stdio.h>
int i = 99;
void f(int i)
{
printf("i = %d\n", i);
}
int main(void)
{
f(8);
return 0;
}
最终输出的结果显示i的值是8而不是99
下边这段内容暂且不说,待以后再说
按变量的存储方式:
静态变量
自动变量
寄存器变量
【所有代码均在windows系统下VC++6.0下运行通过】
(如有错误,敬请指正)