函数
一、函数是什么?
我就简单的说一下,我对函数的理解。函数就是继承“模块程序设计”的一种思想,讲一个复杂的问题“分而治之”,使功能分解,不仅是程序更容易理解,也更容易维护和调试。
二、函数的定义
函数分为标准库函数和自定义函数,标准库函数指的就是string/stdio/math等等这些,我们主要来讲自定义函数。
1、函数的定义
- 格式:
返回值类型 函数名 (类型,形式参数,类型 形式参数2,....)
{
函数体
.....
}
建议:通常变量名用小写字母的单词组合而成;函数名则用大写字母开头的单词组合而成;
若函没有返回值,则需要用viod定义函数返回值的类型;
若函数不惜要入口参数(形参)。则用void代替函数投不中形参表中的内容,表示函数不接收来自调用函数的任何参数;
三、向函数传递值和从函数返回值
1、函数调用
首先声明几个概念:
调用其他函数的函数叫做主调函数;
被调用的函数叫做被调函数;
主调函数把实参的值复制给被调函数的过程叫做参数传递;
#include "stdio.h"
int add(int a, int b)
{
return a+b;
}
int main()
{
int a, b;
int result;
printf("Please input a and b:\n");
scanf("%d%d", &a, &b);
result = add(a, b);
printf("a + b = %d", result);
return 0;
}
结果是:
Please input a and b:
1 5(输入)
a + b = 6(输出)
注意:函数的返回值只能有一个,函数返回值的类型可以是除数组以外的任何类型,函数中的return语句可以有多个,但是不表示函数可以有多个返回值。
良好的程序设计风格要求函数没有返回值也要用“return”作为函数组后一条语句,表示函数执行的结束,但不返回任何值;
return;
2、main()函数
在定义main()函数的时候,未显式的指明返回值类型,也未使用void。是因为他的返回值默认被定为int类型;
void main()
{
....
return;
}
void 指明没有返回值;
int main()
{
...
return 0;
}
int显式指明,return 0相当于exit(0);
int main(void)
{
...
reuturn 0;
}
void显式指明main()函数没有参数;
3、函数原型
在我们定义函数的之后有两种情况:
(1)、将定义的函数放在mian()函数的前面,则就不需要(但是添加了也没错)在程序开头添加函数原型声明;
(2)、将定义的函数放在main()函数的后面,则就需要在程序的开头添加函数原型声明;
#include "stdio.h"
int add(int a, int b) //在main()函数前定义;
{
return a+b;
}
int main()
{
int a, b;
int result;
printf("Please input a and b:\n");
scanf("%d%d", &a, &b);
result = add(a, b);
printf("a + b = %d", result);
return 0;
}
#include "stdio.h"
int add(int a, int b); //在main()函数后定义,需要函数原型声明;
int main()
{
int a, b;
int result;
printf("Please input a and b:\n");
scanf("%d%d", &a, &b);
result = add(a, b);
printf("a + b = %d", result);
return 0;
}
int add(int a, int b)
{
return a+b;
}
4、无符号整形的相关讨论
#include "stdio.h"
unsigned int Fact(unsigned int n)
{
printf("n = %u\n", n);
if(n < 0)
{
return -1;
}
else
{
return n + 1;
}
}
int main()
{
int a, result;
printf("Please input a:\n");
scanf("%d", &a);
result = Fact(a);
printf("%d", result);
return 0;
}
Please input a:
-1(输入)
n = 4294967295(输出)
0
Please input a:
1(输入)
n = 1(输出)
2
首先无符号整形数是永远不可能成为负值的,当程序给被调函数传给一个-1时,他不会执行if(i<0)的语句,只会执行第二个语句,而且十进制无符号输出则是一堆莫名的数串,但是输入正数的时候,一切似乎又恢复了正常;
四、函数的递归调用和递归函数
函数内自己调用自己的的函数调用就是递归调用;
而这个函数就是递归函数;
#include "stdio.h"
long Fact(int n)
{
if(n<0)
{
return -1;
}
if(n==0 || n==1)
{
return 1;
}
if(n>1)
{
return (Fact(n-1) * n);
}
}
int main()
{
int a;
long result;
printf("Please input a number:\n");
scanf("%d", &a);
result = Fact(a);
if(result == -1)
{
printf("Input error!\n");
}
else
{
printf("%d! = %ld", a, result);
}
return 0;
}
结果是:
Please input a number:
10(输入)
10! = 3628800(输出)
五、变量的作用域和储存类型
1、变量的作用域
- 程序中被花括号括起来的区域,叫做语句块(函数体,分支语句,循环体…);
- 不在任何语句块中定义的变量,称为全局变量;
- 在除整个程序以外的其他语句块内定义的变量,称为局部变量;
2、变量额存储类型
- 变量定义的一般形式:存储类型 数据类型 变量名;
- c语言主要提供以下几种类型的存储类型
- 自动变量
- 静态变量
- 外部变量
- 寄存器变量
自动变量
如果没有指定变量的存储类型,那么变量的存储类型就缺省为auto;
#include "stdio.h"
long Func(int a)
{
auto long p = 1;
p = p * a;
return p;
}
int main()
{
int i, n;
printf("Please input:\n");
scanf("%d", &n);
for(i=0;i<n;i++)
{
printf("%d! = %ld\n", i, Func(i));
}
return 0;
}
结果是:
Please input:
10(输入)
0! = 0(输出)
1! = 1
2! = 2
3! = 3
4! = 4
5! = 5
6! = 6
7! = 7
8! = 8
9! = 9
- 自动变量在定义的时候不会自定初始化,所以需要程序员在程序中显式的指定初始值,否则自定变量的值是不确定的;
- 自动变量时默认添加的,也就是说你不显示的添加的话,这个变量本身就是一个自动变量;
- 自动变量在退出函数之后,其分配的内存立即会被释放,这个相对于静态变量来说对比会非常明显,下一次使用该变量的时候会重新分配内存,所以不会保持上一次退出函数时的值;
静态变量(相对于自定变量而言)
#include "stdio.h"
long Func(int a)
{
static long p = 1;
p = p * a;
return p;
}
int main()
{
int i, n;
printf("Please input:\n");
scanf("%d", &n);
for(i=1;i<n;i++)
{
printf("%d! = %ld\n", i, Func(i));
}
return 0;
}
结果是:
Please input:
10(输入)
1! = 1(输出)
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
- 静态变量是与程序共存亡的,而自动变量是与程序块共存亡;
- 上面函数中我们定义的是静态局部变量,只能在被定义的函数内被访问;除此之外,还有静态全局变量,可以在定义它的文件的任何地方访问;
- 静态局部变量在退出函数后能保持其值到下一次进入函数,其占据的内存在退出函数后不会被释放,直到程序结束!
外部变量
如果在函数之外定义的变量没有指定其储存类型,那么他就是一个外部变量;外部变量就是一个全局变量。
格式:
extern 类型名 变量名
外部变量的初始化:
1、初始化表达式必须是常量表达式;
2、只能初始化一次;
3、在不进行显示初始化的情况下,外部变量将被初始化为0;
外部变量的作用域:
1、从声明它的地方开始,到其所在的文件结尾结束;
2、如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个文件夹中,则必须在相应的变量声明中强制地使用关键字extern.
寄存器变量
格式:
ragister 类型名 变量名
当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。
1.只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量。
2.register是一个”建议”型关键字,意指程序建议该变量放在寄存器中,但最终该变量可能因为条件不满足并未成为寄存器变量,而是被放在了存储器中,但编译器中并不报错(在C++语言中有另一个”建议”型关键字:inline)。
下面是一个采用寄存器变量的例子:
/* 求1+2+3+….+n的值 */
WORD Addition(BYTE n)
{
register i,s=0;
for(i=1;i<=n;i++)
{
s=s+i;
}
return s;
}
本程序循环n次,i和s都被频繁使用,因此可定义为寄存器变量。
寄存器变量没有地址,所以使用&i 是错误的