为了实现某些功能,我们可以自己编写函数,到用的时候打个函数名上去就能直接使用,这样能减少重复性操作,减少代码量,也有利于程序的模块化。
定义函数
函数的格式:
函数类型 函数名称(形式参数1,形式参数2...)
{
函数的执行体
}
- 函数类型:根据函数的返回值来判断。比如,如果函数最终会产生并返回一个整型数,那函数类型就是
int
;如果只是输出打印一些东西,没有返回值,那函数类型就是void
- 函数名称:自定,最好能体现函数能实现什么功能
- 形式参数:可有可无,具体看定义的函数有什么功能。参数要声明类型
例1:定义一个能打印“Hello World!”的函数
#include <stdio.h>
void PrintHelloWorld(void)
{
printf("Hello World!\n");
}
int main(void)
{
PrintHelloWorld();
return 0;
}
例2:定义一个函数,返回某个整型数+1后的值
#include <stdio.h>
int AddOne(int x)//小括号里面的是形式参数
{
x = x + 1;
return x;
}
int main(void)
{
int x = 100;
x = AddOne(x);
printf("+1后的值是:%d",x);
}
例二中,AddOne
函数小括号里面的是形式参数,这里的x
仅限在AddOne
函数里面使用,而且它和main()
函数里面的x
在内存中的地址不同,因此两个x
不会冲突。
函数中的return
则是用于终止函数,并向主调函数返回一个值。return
可有可无,具体看定义的函数要实现什么功能
补充
-
自定义函数一般都要写在
main()
函数前面#include <stdio.h> 函数类型 函数名称(参数1,参数2...) { 函数体 } int main(void) { ... return 0; }
不过有些编译器会做优化,自定义函数写在main函数后面也可以。如果编译器没做这方面的优化,也可以先在main函数前声明一下自定的函数,然后再在main函数后面写编写这个函数:
#include <stdio.h> 函数类型 函数名称(参数1,参数2...);//注意这里有个分号 int main(void) { ... return 0; } 函数类型 函数名称(参数1,参数2...) { 函数体 }
-
在自定义函数中的形式参数,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数中的形式参数会被临时分配内存单元。当函数调用结束后,形参单元会被释放。
-
当形式参数名称和
main()
里的某个变量名称一样时:#include <stdio.h> void func(int a) { a = a + 100; printf("这里是func函数:a=%d\n",a); } int main(void) { int a = 100; func(a); printf("这里是main函数:a=%d\n",a); return 0; }
输出结果:
这里是func函数:a=200 这里是main函数:a=100
可以看出,尽管
func()
中的形式参数名称也是a
,但是在func()
中对形式参数a
的操作并不影响main()
里面的变量a
。事实上,传进
func()
中的只是main()
里面的变量a
的数值,而不是地址。main()
里面的变量a
和func()
中的形式参数a
,两者在内存中的地址并不同,因此对其中一个的操作并不会影响到另一个的值。下面看第4点,以数组为例说明如果向自定义函数传入地址会怎么样
-
如果形式参数设为数组,则在调用改函数时传进去的只是这个数组的首地址,因此说形式参数不存在数组的概念:
#include <stdio.h> void func(int Array[]) { printf("%d\n",sizeof(Array)); } int main(void) { int data[] = {1,2,3,4,5}; printf("%d\n",sizeof(data));//输出结果是20 func(data);//输出结果是8 return 0; }
在上方例子中,自定义函数时尽管设了个数组作为形式参数,但实际上这个参数并非真正的数组,而只是数组的首地址。
func()
中其实执行的是“sizeof(data数组的首地址)
”,输出结果是8(因为操作系统中是用8个字节来表示一个地址)。而正是因为在调用
func()
时,传进func()
里去的是数组的首地址,因此func()
对数组的操作其实就是对main()
里面的data
数组进行操作:#include <stdio.h> void func(int Array[]) { Array[3] = 8; puts("这里是func函数,data数组是:"); for(int i=0;i<5;i++) printf("%d ",Array[i]); printf("\n"); } int main(void) { int data[] = {1,2,3,4,5}; func(data); puts("这里是main函数,data数组是:"); for(int i=0;i<5;i++) printf("%d ",data[i]); return 0; }
输出结果:
这里是func函数,data数组是: 1 2 3 8 5 这里是main函数,data数组是: 1 2 3 8 5
可以看到,
func()
是真的会直接影响到main()
中的数组data
。与补充第3点不同,第3点中传入自定义函数的只是变量的数值(而不是变量的地址)。 -
如果参数是二维数组要注意正确写法:第二个中括号里面不能省略数字。例:
int arr[2][3]
或int arr[][3]
-
如果使用函数递归(递归例子见下面的例二),一定要避免陷入死循环
-
函数返回值的类型其实也是函数的类型,如果返回值类型和函数类型不同,则以函数类型为准:
int f(void) { return 10.5; }
因为函数
f()
类型是int
,所以当f()
被调用后,最终返回的值是10,而不是10.5。因此返回值要注意类型,如果类型不同,可能会发生强制转换从而影响结果,或出现编译警告。 -
只要函数中的
return
语句被执行,则return
所在的函数就会被终止 -
一个程序有且只能有一个主函数
main()
-
一个函数的功能应该尽量独立,单一
-
函数也可以分类为库函数和自定义函数,平时所用的
printf()
就是库函数。对库函数的声明是通过#include <stdio.h>
来实现的std
是standard(标准),i
是input(输入),o
是output(输出),h
是header(头文件) -
函数是C语言的基本单位,类是Java,C#,C++的基本单位
小例子
例一:定义一个打印整形数组的函数
#include <stdio.h>
void PrintArray(int ArrayName[],int ArraySize)
{
for(int i=0; i<ArraySize; i++)
{
printf("%d ",ArrayName[i]);
}
printf("\n");
}
int main(void)
{
int data[] = {1,2,3,4,5};
int length = sizeof(data)/sizeof(data[0]);
PrintArray(data,length);
}
例二:有一个数列:10,12,14,16,18,20…,输入第几项(n),输出第n项的数值
#include <stdio.h>
int getVal(int n)
{
if(n==1)
return 10;
else
{
n--;
return getVal(n)+2;
}
}
int main()
{
int num;
puts("请输入第几项(n):");
scanf("%d",&num);
printf("第%d项是:%d",num,getVal(num));
return 0;
}
例三:求n!
#include <stdio.h>
int FactorialOf(int n)
{
if(n==1 || n==0)
return 1;
else
return n * FactorialOf(n-1);
}
int main(void)
{
int num;
puts("请输入要求的阶乘数:");
scanf("%d",&num);
printf("%d的阶乘结果是:%d\n",num,FactorialOf(num));
return 0;
}
在32位的计算机中,本例最多只能求16的阶乘,求17的阶乘结果就会不准确,因为int类型最大值只有2147483647(231-1)