函数

  大家好,简单的介绍一下我自己,我是小猪Ray

  码字之前想跟大家分享一个,算是故事吧:当时在逛知乎,看到有大神在讨论自己写过的小程序,里面有一个大神说他在冬天觉得天气太冷,自己就给小米写了一个死循环的小程序,让小米发烧当暖手宝(我是不是改收广告费0_0)。其实仔细想想我们自己学了绝对也能做到,用编程来改变生活,这不就是咱们这个时代在做的事情吗?

  函数

function_name()

{  }

这个函数是最简单的函数,当他被调用时简单地返回。然而它可以实现一种有用的存根目的,为那些还没有实现的代码保留一个位置。(就是咱们大一同学们学习热情比较高的时候所实行的占位行为)

一、 函数的声明

函数的声明就是当 编译器遇到一个函数调用时,它产生的代码船体参数并调用这个函数,并且接受该函数的返回的值(如果有的话)。也就是说你想使用函数前得给它一个声明,让编译器知道你要使用函数啦~就像打怪兽出大招前那些个英雄非要把大招的名字大喊一遍再放一样。


1.原型

原型即使总结了函数定义的起始部分的声明,向编译器提供有关该函数应该如何调用的完整信息。也就是说原型会告诉编译器函数的参数数量和每个参数的类型以及返回值的类型。


标准表示,在同一个代码中,函数原型必须与同一个函数的任何先前原型相匹配,否则编译器应该产生一种错误信息,一起看一下下面一种使用函数原型的危险方法:

Void

a()

{

        int       *func(int     *value,int      len);

         ...

}

void

b()

{

         int         *func(int     *value,int     len);

         ...

}

我们仔细看一下这两个原型,会发现他们是不一样的。参数的顺序倒了,返回类型也不同。问题在于两个函数原型都写于函数体的内部,他们都具有代码块作用域,所以编译器再每个函数结束前会把它记住的原型信息丢弃,这样就无法发现他们之间存在的不匹配情况。

有问题自然就有解决的办法啦,世界上缺少的不是答案,而是问题呀!

下面的代码说明了一种使用函数原型的更好办法。

#include “func.h”

void 

a()

{

      ...

}

void

b()

{

...

}

文件func.h包含了下面的函数原型

int     *func( int    *value,int   len );

(总结上面的函数,是把原型至于一个单独的文件,当其他源文件需要这个函数的原型时,就使用#include 指令包含该文件 )


相比之下这个的好处有

1 下面的函数原型具有文件作用域,所以原型的一份拷贝可以作用于整个源文件。

2 下面的函数原型只书写了一次,这样就不会出现多分原型的拷贝之间的不匹配现象。

3.如果函数的定义进行了修改,我们只需要修改原型,并重新编译所有包含了该原型的源文件即可。

4.如果函数的原型同时被#include 指令包含到定义函数的文件中,编译器即可确认函数原型于函数定义的匹配啦~


2.函数的缺省定义

当程序调用一个无法见到原型的函数时候,编译器便认为该函数返回一个整型值。

看看下面这个函数

float   f;

...

f  = xyz ();

如果在函数调用之前编译器无法看到它的原型,就认定这个函数返回一个整型值,并产生指令将这个值转换为floata,然后再赋值给变量f。

这里有个问题就是为什么函数的返回值实际上已经是浮点值的形式时,还要执行类型转换呢?

因为我们是人,我们知道我们要干什么,但是编译器不知道啊,因为我们没有个给原型或者声明告诉它这些信息。

3.函数的参数

在这里我们先搞清楚形参和实参

形参就是实参的一个拷贝啦,仅此而已,拷贝后就会它的一个副本,我们对副本进行操作并不会影响本体哦~就这样想,我用克隆技术克隆出另一个你,他就是你的副本(你的形参),然后咱们打它,咱们蹂躏它o(=  - = )o,这个时候你还是你吧,不会少块肉吧(身为建设社会主义的好青年咱们还是不要做这种事比较好)

那事情就是这样的,函数的参数是通过传值的方式进行传递的,它实际所传递的是实参的一份拷贝。因此函数可以修改它的形参(也就是实参的拷贝),而不会修改调用程序实际传递的参数。数组名也是通过传值的方式传递的,但它传递给函数的是一个只想该数组的指针的拷贝(个人觉得这个挺重要的,后面的数组会再提啦)在函数中,如果再数组形参中使用了下标引用操作,就会引发间接访问操作,她时机所访问的是调用程序的数组元素。因此,再函数种修改参数数组的元素时机上修改的是调用程序的数组。这个就是传说中的传制调用啦~~

咱们再一起看看个简单的小程序

无效的整数交换

void 

swap(int  x,   int  y )

{

int   temp;

temp  = x  ;

x   =  y ;

y   =  temp ;

}

我们看得出来这个代码是想要交换调用程序所传递的这两个参数的值。但是你操作的至少原先参数的拷贝是形参,也就是对你的克隆体进行操作,都没有打到你身上怎么可能会痛啊  -  0  -

为了访问调用程序的值,咱们必须向函数传递只想咱们希望修改的变量的指针,接着函数对指针使用间接访问操作。也就是我给别人你的坐标,让他去打你,放过你的克隆体,你感到痛就对了,哈哈哈

所以有效的程序是

void 

swap(int    *x,   int    *y )

{

int  temp;

temp  =  * x;

* x  = * y;

* y = temp;

}

下面有几个小知识点注意一下咯

数组的参数的值是一个指针,下标引用的实际上是对这个指针执行间接访问操作。

再声明函数参数时不指定它的长度是合法的,因为函数并不为数组元素分配内存。

好了,今天就分享这么多了,第一次码字就码了这么多,后面还有递归迭代函数战争,留着下次吧

对了,小猪学习的参考书是《C于指针》,自学中呢  这本书确实很不错,该有的都有了,推荐大家购买,当然我希望在盗版书横行市场的情况下,大家还是多支持支持正版,咱们就是版权意识太薄弱,这样下去咱们的好书将会越来越少啊。

by the way ~小编也是个音乐爱好者,所以我如果能找到我喜欢的歌的链接我都会放上来,今天的音乐是浪费,特别好听的一首歌,一年间听了好多次,大家可以找找原版的听,林宥嘉的,在网易云就有。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值