C语言函数的特点和作用,C语言函数讲解(一)

C语言函数(一)

谨记

短暂的人生中,注定有很多人陪伴着你度过,陪你一起欢呼,陪你一起伤心流泪,陪你一起走过一条又一条的街道,或许他们并不是真真切切的,但是至少他们还在你身边。这个世界很大,但是却又很小,大的时候你要找到的你想要的却始终也找不到的,小的时候你要逃避或者不想要的却始终在你身边徘徊着,曾经有一段时间,自己很堕落,不知道干嘛,每天就浑浑噩噩的过完一天又一天,还把自己的笔名换成“徘徊的小青年”,那个时候,整个人生已经失去意义了,知道后来有一天看到了一个画面,我才明白,其实还有很多东西是需要自己努力去奋斗的,请你一定要记住,不管你做什么,身处何境,请不要抱怨,相信自己,你会成功的。

前言

非常的抱歉,由于其他事耽误了,文章没有及时的更新,望各位读者体谅。今天给大家带来的是咋们C语言中很重要的一个知识点——函数,前面我们对指针已经有一个深刻的理解了,那么,下面我们来看看函数吧,通过本篇文章你可以学到:

函数的定义和声明

函数的调用、传参和返回值

函数的分类展示

函数和数组

main函数机制

函数的定义和声明

1、函数的定义

函数是一个完成特定功能的代码模块,通常有参数,也可以没有参数;通常要求有返回值,也可以是空值。一般形式如下:

( )

{

语句序列;

return[()];

}

函数名称是一个标识符,要求符合标识符的命名规则。

数据类型是整个函数返回值的类型。这里可以包括存储类型说明符、数据类型说明符以及时间域说明符。如果函数不需要有返回值时,函数类型说明符可以写为void。

形式参数说明就是形式参数的列表,简称形参。形参可以是任意类型的变量,各参数之间用逗号分隔。在进行函数调用时,调用函数将赋予这些形式参数实际的值。

函数名后有一对花括弧。“{}”中的内容称为函数体。函数体中有若干条语句(大于或等于零个),实现特定的功能。注意:在函数体中,表达式语句里使用的变量必须事先已有说明,否则不能使用。

return[()]语句中表达式的值,要求和函数名前的数据类型保持一致。如果数据类型为void,可以省略,也可以写成“return;”。

例如定义一个简单的函数

无形参的函数:

void test(){

printf("这是void一个函数\n");

}

有形参的函数

void test(int a){

int b = 20;

b = b + a;

printf("%d\n",b);

}

2、函数的声明

函数的声明就是把函数名、返回值类型以及形参类型、个数和顺序通知编译系统,以便在调用该函数时,编译系统进行对照检查,包括函数名是否正确、传递参数的类型、个数是否与形参一致。如若出现不对应的情况,编译会有语法错误。

在C语言中,用户可以通过两种方法来进行函数声明。

① 如果函数调用前,没有对函数作声明,且同一源文件的前面出现了该函数的定义,那么编译器就会记住它的参数数量和类型以及函数的返回值类型,即把它作为函数的声明,并将函数返回值的类型默认为int型。

② 如果在同一源文件的前面没有该函数的定义,则需要提供该函数的函数原型。用户自定义的函数原型通常可以一起写在头文件中,通过头文件引用的方式来声明。

提示;实际上,如果在调用函数之前没有对函数进行声明,则编译系统会把第一次遇到的该函数形式(函数定义或函数调用)作为函数的声明,并将函数返回值的类型默认为int型。

在前面的内容中介绍过变量的声明。对于全局变量的声明可以加上extern标识,同样对于函数的声明,也可以使用extern。如果函数的声明中带有关键字extern,是告诉编译器这个函数是在别的源文件里定义的。还有一个关键字static,若全局变量前有static修饰,则变量只能在当前文件中被使用,同样若函数的声明前面有static限制,是告诉编译器,该函数只能在当前文件中被调用,外部文件不能调用。

1、函数的分类

对于函数的分类,我们可以从返回值、形参进行一个分类,一般分为4类:

没有返回值和形参

没有返回值,有形参

有返回值没有形参

有返回值,有形参

接下来分别写一个简单的示例代码看看这四种函数的写法

#include

//=======函数的声明部分========

void test();

void test1(int a);

int test2();

int test3(int a,int b);

//========主函数===========

int main(int argc, const char * argv[]) {

//======这里可以调用函数

printf("Hello, World!\n");

return 0;

}

//=======函数的实现部分========

void test(){

printf("这是没有返回值,没有参数的函数\n");

}

void test1(int a){

int k = 10;

k = k + a;

printf("%d\n",k);

printf("这是没有返回值,有形参的函数\n");

}

int test2(){

printf("有函数返回值,没有形参的函数\n");

return 10;

}

int test3(int a,int b){

printf("有函数返回值,有形参的函数\n");

return a + b;

}

在实际开发中,需要哪一种函数,就可以去自己按照函数的形式进行声明和实现那种函数。

函数的调用、参数的传递和返回值

1、函数的调用

函数的使用也叫函数的调用,形式如下:

函数名称(实际参数)

注意:

是一个标识符,符合标识符的命名规则;

(实际参数)需要确切的数据,也可以是具有确定值的表达式。实参就是在使用函数时,调用函数传递给被调用函数的数据,用以完成所要求的任务。

函数的参数分为形式参数和实际参数两种。

形式参数指的是出现在函数定义中的参数列表,简称形参。实际参数简称实参,出现在主调函数中。发生函数调用时,主调函数把实参的值传送给被调函数的形参,从而实现主调函数向被调函数的数据传送。

提示

函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句。

如果是调用无参函数,则实参列表可以没有,但括弧不能省略。如果实参列表包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应一致。实参与形参按顺序对应,一一传递数据。这里,对实参列表取值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序。

示例代码:

#include

//=======函数的声明部分========

void test();

//========主函数===========

int main(int argc, const char * argv[]) {

//======这里可以调用函数

test();

return 0;

}

//=======函数的实现部分========

void test(){

printf("这是没有返回值,没有参数的函数\n");

}

上面的写法,相当于把函数的定义写在了调用语句之前,test函数没有参数,也没有返回值。当被调用的函数有参数时,就涉及了参数的传递问题。

注意事项:被调函数必须是已经声明了的函数,或者被调函数的位置位于调用函数之前。

在有形参的时候,函数调用的时候就会传一个确切的实参来替代形参,这里要注意的是函数调用一般有3种用途:

① 函数语句:把函数调用作为一个语句。这时不要求函数带返回值,只要求函数完成一定的操作,如:

printf("Hello C world\n");

② 函数表达式:函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数返回一个确定的值以参加表达式的运算,如:

sum1 = sum(a,b);

③ 函数参数:函数调用作为一个函数的实参。函数调用作为函数的参数,实质上也是函数表达式形式调用的一种,因为函数的参数本来就要求是表达式形式,如:

printf("a和b的和是: %d\n",sum(a, b));

2、函数的参数传递

函数的参数传递,可以分为3种方式:

值传递

地址传递

全局变量传递

2.1、值传递

#include

void exchange(int a, int b){

int t;

printf("&a=%p &b=%p\n", &a, &b);

t = a;

a = b;

b = t;

printf("a=%d b=%d\n\n", a, b);

}

int main(int argc, const char * argv[]) {

int m = 10, n = 20;

exchange(m, n);

printf("&m=%p &n=%p\n", &m, &n);

printf("m=%d n=%d\n", m, n);

return 0;

}

输出结果:

&a=0xbfcff4f0 &b=0xbfcff4f4

a=20 b=10

&m=0xbfcff50c &n=0xbfcff508

m=10 n=20

Program ended with exit code: 0

在该程序中,main函数调用了exchange函数。在exchange函数的定义中,参数a、b是形参,它在exchange函数体内都可以使用,离开函数体,则不能使用。主调函数是main函数,调用语句是“exchange(m,n)”,其中的m就是实参,且m是局部变量,只能在main函数体内使用。在执行了函数调用后,m的值就传给了形参a,这个传参过程自动进行,相当于执行了代码“int a = m; int b = n”。形参和实参是两个不同的变量,占用不同的存储空间,因此,当形参的值发生变化时,并不影响实参的值。

函数的形参和实参具有以下特点:

a.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。调用结束返回主调函数后则不能再使用该形参变量。

b.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应先用赋值、输入等办法使实参获得确定值。

c.实参和形参在数量上、类型上、顺序上应严格一致,否则会发生“类型不匹配”的错误。

从图8-1中可以看出,实参和形参所占用的存储单元完全是独立的。在函数调用时,实参把存储单元中的数据赋值给形参的存储单元;而在函数调用后,若形参的值发生了改变,它也无法传递给实参(由于参数的传递是单向的,只能从实参传递给形参)。

2.2、地址传递

当你希望通过形参来改变实参的值的时候,那么你可以用地址传递,因为涉及到地址,其实就是我们以前说的指针,对于操作指针,是不是就相当于在操作指针指向的内存地址里面的对象。

#include

void exchange(int *p, int *q){

int t;

printf("&p=%p &q=%p p=%p q=%p\n", &p, &q, p, q);

t = *p;

*p = *q;

*q = t;

printf("*p=%d *q=%d\n\n", *p, *q);

}

int main(int argc, const char * argv[]) {

int m = 10, n = 20;

exchange (&m, &n);

printf ("&m=%p &n=%p\n", &m, &n);

printf ("m=%d n=%d\n", m, n);

return 0;

}

输出结果:

&p=0xbfeb67c0 &q=0xbfeb67c4 p=0xbfeb67dc q=0xbfeb67d8

*p=20 *q=10

&m=0xbfeb67dc &n=0xbfeb67d8

m=20 n=10

Program ended with exit code: 0

在该程序中,实参是&m和&n,形参是两个指针。传参的过程相当于两条语句“int p = &m; int q = &n;”。在函数exchange中,交换了p和q,即交换了p和q的目标,交换了变量m和n的值。在C程序中,通过传递变量的地址,可以起到改变主调函数中变量的作用。

2.3、全局变量传参

全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的。实际上,全局变量也是一种静态型的变量。将它初始化为0。全局变量一经定义后就会在程序的任何地方可见。使用全局变量传递数据的先后顺序的不同会影响计算结果,应用顺序不当,会导致错误,这种方式尽量少用。(建议不要用)。

#include

int n;//全局变量

double factorial();

int main(int argc, const char * argv[]) {

double s = 0;

printf("input:");

scanf("%d", &n);

s = factorial();

printf("%e\n", s);

return 0;

}

double factorial(){

double ret = 1;

int i;

for (i = 1; i <= n; i++)

ret *= i;

return ret;

}

输出结果:

input:15

1.307674e+12

Program ended with exit code: 0

3、函数的返回值

函数的返回值是指被调用函数返回给调用函数的值。

对于函数的返回值,可以通过以下几点解释:

① 函数的返回值只能通过return语句返回主调函数,return 语句的一般形式为

return 表达式;

或者

return (表达式);

该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句,但每次调用只能有一个return语句被执行,因此只能返回一个值。

② 函数返回值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数定义中的类型为准,自动进行类型转换。

③ 如函数返回值为整型,在函数定义时可以省去类型说明。

④ 没有返回值的函数,可以明确定义为空类型,类型说明符为void。

#include

int fun(int n);

int main(int argc, const char * argv[]) {

int sum = 0, n;

printf("input:");

scanf("%d", &n);

s = fun(n);

printf("1+2+…+%d=%n\n", n, sum);

return 0;

}

int fun(int n){

int i, sum = 0

for (i = 1; i <= n; i++)

sum += i;

return sum;

}

输出结果:

input:100

1+2+…+100=5050

Program ended with exit code: 0

当然,对于函数,在C99标准下,系统还给我们提供了很多函数,对于那些函数我们成为库函数,我们在程序中可以直接调用,对于库函数,可以参考https://baike.baidu.com/item/C语言函数参考手册/3866969?fr=aladdin

函数和数组之间的联系

1、传递数组

当形参是数组形式时,其本质上也是一个指针。数组作为实参传递时,形参并没有复制实参所有的内容,而是复制了实参数组的首地址。由于数组的特殊性,只要知道了数组的首地址,就可以依次访问数组中的所有元素。

#include

int test_array(int a[], int n, int *p){

int i, sum = 0;

*p = 0;

for (i = 0; i < n; i++)

{

sum += a[i];

if (a[i] % 2)

(*p)++;

}

return sum;

}

int main(int argc, const char * argv[]) {

int a[] = {9, 12, 2, 3, 29, 31, 40, 80}, n;

int sum = 0, odd= 0;

n = sizeof(a) / sizeof(int);

sum = test_array(a, n, &odd);

printf("sum=%d add numbers count =%d\n",

sum, odd);

return 0;

}

输出结果:

sum=206 add numbers count =4

Program ended with exit code: 0

在该程序中,子函数用来计算主调函数中的整型数组的元素的和,并统计数组中奇数的个数。实参传的是数组名、数组的元素个数及一个整型的指针(除了传递数组名以外,还要传递数组中元素的个数)。形参是一维数组的形式,在这里,读者要特别注意:当形参是数组形式时,本质是同级别的指针。该程序中的形参int a[],实际上是int *a。上面程序演示的是一维数组,对于二维数组这里就不演示了,其实差不多。读者可以自行去摸索。

温馨提示:当形参是数组形式时,本质是同级别的指针。

2、传递指针

前面介绍了函数形参是数组形式的用法,下面介绍另一种很常见的写法即传递指针。其实,读者如果熟练掌握了上一章中数组和指针部分的内容,这里就很容易理解。若需要给子函数传递一维数组,可以写成下面的形式:

int test_array(int *a, int n, int p)

若需要给子函数传递二维数组,应该写成下面的形式:

int test_array(int n, int m, int (a)[m], int p)

在这种形式中,int (a)[m]是一种数组指针或称为行指针,指针加1,移动m个数据,和m列的二维数组名是类似的。这就就提这么多,下篇文章会详细讲解。

main函数的参数介绍

普通函数可以带参数,其实,main函数也可以带参数。当执行程序时,也可以在命令行上给main函数传参。完整的main函数原型如下:

int main(int argc, const char *argv[])

上面是数组的形式,也可以写成指针的形式:

int main(int argc, char **argv)

其中,argc是传给main函数的参数的个数,argv指向具体的参数。

#include

int main(int argc,const char *argv[]){

int i;

printf("argc=%d\n", argc);

for (i = 0; i < argc; i++)

printf("argv[%d]=%s\n", i, argv[i]);

return 0;

}

输出结果:

argc=1

argv[0]=...(这里打印的是路径)Build/Products/Debug/函数的简单示例

Program ended with exit code: 0

总结

这篇文章对刚接触C语言函数的读者应该有一个很好的帮助,这篇文章将的是基础和理论,下一篇文章将讲解函数的重点,当然也希望读者能够认真的阅读,这里面有很多细节的,望读者揣摩。

结尾

希望读者真诚的对待每一件事情,每天都能学到新的知识点,要记住,认识短暂,开心也是过一天,不开心也是一天,无所事事也是一天,小小收获也是一天,所以,你的路你自己选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值