非典型性C语言教程-1.2 函数

函数是C语言的基本单位,C语言就是由一组函数构成的。

首先要明确的一个概念是函数本质上是一段代码的入口地址。函数名的语言就是这个入口地址。比如你可以写如下程序:

#include

int ff(int n)

{

int ret=1;

return ret;

}

int main( void )

{

ff(4);

printf("ff's address is %x/n", ff);

}



编 译这段代码VC8会给一个警告: warning C4313: “printf”: 格式字符串中的“%x”与参数 1 (属于“int (__cdecl *)(int)”类型)冲突。 可以看出ff这个函数名的类型实际上是int (__cdecl *)(int)。
复习一下函数指针的定义,发现函数名ff的类型就是一个函数指针,唯一不太明确的地方是“__cdecl”。这牵涉到函数调用时参数的传递方式。

前面在讲局部变量时说到过局部变量分配。( 本来想添加一个指向1.1变量的链接,但是由于blogspot.com仍然被和谐着,用的Web代理连接上的话地址可能被重新解释,所以还是等到放过blogspot.com的时候再修改吧。) 局部变量分配在栈里面,函数调用开始时栈的结构是栈顶是局部变量,然后是返回地址。返回地址下面是参数。函数所需要的参数是函数的调用者在函数调用之前压 入栈的。函数返回的时候就牵涉到一个谁负责清除这个参数的问题。于是就有两种调用约定。在MSDN中可以查到__cdecl和__stdcall,还有C ++的一种调用函数的方式。两种都是把参数从右自左压栈,区别是__cdecl是函数调用者负责清除参数,而__stdcall则是函数自己负责清除参 数。C语言默认是__cdecl调用规定,但是Windows的API都是以__stdcall提供的。在以前Windows的SDK中这个调用约定写做 PASCAL 或者是STDCALL, 这两个宏都的实际内容都是__stdcall。

两种调用方式都是从右自左压入参数的,看看下面的代码会输出什么?

#include

int main( void )

{

int a=1., b=4, c=8

printf("a=%d, b=%d, c=%d/n", a++, b=c+a, c=a++);

}

从 右自左压栈,就意味着表达式的解析是从右开始的。上面的代码首先计算表达式c=a++,这个表达式是先取a的值赋值给c,然后把a=a+1。此时a=2, c=1。所以第二个表达式b=c+a的值是3,且b=3。最后计算的表达式是a++。此时先取a的值2,然后a=a+1=3了。

当然这种代码是应该尽量避免的。 代码主要是写给人看的而不是主要写给机器看的。写一些让人不好理解的代码并不能炫耀你对语言掌握的好,只会让后面读你代码的人不自觉的开始咒骂你。

函 数还有两个重要的概念,就是实参(Argument) 和 形参(Parameters)。C,还有C++, Java以及C#的参数传递方式都是值拷贝的方式。上面的一个例子中, main函数调用了ff函数,传入了一个参数4,这个参数在main函数的栈空间里面,然后复制到栈中,给ff函数使用。参数4就是实参,而ff函数中的 n表示的就是栈中压入的实参的副本形参。这个概念应该讲的很多了。

下一个讲表达式。 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值