很久以前写的旧文,不改了,直接发出来
---------------
这个帖子分以下几个部分:
1,声明和定义
2,参数传递方法
3,参数入栈顺序
4,指针作为参数
5,数组作为参数
6,结构体作为参数
1,函数声明和定义:
首先,分清楚声明和定义。
在一个函数声明中,你需要给出函数名,函数返回值类型(如果有的话),以及在调用这个函数时
必须提供的参数个数及类型。比如:
elem * next_elem();
char * strcpy(char *to ,const char *from);
void exit(int);
在声明中,只有以下三项是必需的:
a.函数名
b.返回值类型
c.参数个数及类型
定义:
在程序调用的每个函数都应被定义(在某个地方,且仅仅定义一次),
函数定义也就是一个给出了函数体的函数声明
比如:
void plus(int a); //声明
void plus(int a)
{ //给出了函数体的声明,也就是定义
++a;
}
注意:同一个函数的定义和声明和定义必须一致:
即:
a.函数名一致
b.返回值类型一致
c.参数个数及类型一致
并没有把参数名称作为必须一致的一部分
2,参数传递方法:
函数参数传递方法不外下边的三种:
a,通过寄存器
b,通过全局变量指针
c,通过栈
c中函数参数传递一般通过栈
如下代码:
#include <stdio.h>
void plus(int b);
#ifdef MAIN
int main(void)
{
int a = 0;
plus(a);
printf("a = %d", a);
return 0;
}
#endif
void plus(int b)
{ ++b;
}
有朋友已经看出来了
void plus(int b)
{ ++b;
}
plus这个函数根本不能改变函数外的任何变量
为什么呢
因为c中参数是通过栈传递的,在main中调用plus(a)时
main中的a 精确副本被压入栈,然后,进入到plus以后,pop出来
然后,复制到局部变量b(也在栈中)的空间
再对b执行++b;
然后,函数结束时,b所占用的栈的空间被释放
然后,就等于什么 也没干
所以,从这个意义上来说,c的函数调用时,参数传递是单向的
3,参数入栈顺序:
不一定
有从左向右入栈的,有从右向左入栈
比如下边的程序:
#include <stdio.h>
void fun(int a,int b,int c)
{
printf(" a = %d/n",a);
printf(" b = %d/n",b);
printf(" c = %d/n",c);
}
int main(void)
{
int c;
c = 0;
fun(c,++c,++c);
return 0;
}
有可能在不同的编译器上得出不同的结果。
详情可参看:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core___stdcall.asp
4,指针作为参数
指针这个玩意儿有点与别人不一样
由于指针是一个变量,存放了另外一个变量的地址
当这个地址被当作参数,传入到函数里以后,我们就可以按图索骥来找
这个地址里的东西,胡作非为一番了
#include <stdio.h>
void plus(int *b);
#ifdef MAIN
int main(void)
{
int a = 0;
plus(&a);
printf("a = %d", a);
return 0;
}
#endif
void plus(int *b)
{ ++ *b;
}
void swap(int *v1,int *v2)
{
int vt = *v1;
*v2 = vt;
*v1 = *v1;
}
5,数组作为参数:
数组作为函数的参数,传递的是数组的首元素的地址
(数组当作参数时,退化为指针)
如:
#include <stdio.h>
#include <stdio.h>
#ifdef MAIN
int main(void)
{
int len;
char str[] = "an array";
len =strlen(str);
printf("len of str is :%d",len);
return 0;
}
#endif
所以,在被调用的函数内部,数组参数的大小是不可用的
6,结构体作为参数:
跟一般的参数没有两样,但是当结构体很大时,
传递可能要占用较大的空间,可以考虑只传入结构体的指针
ps:
上边有#ifdef MAIN的代码,可以用gcc -o output.exe -DMAIN input.c来编译
-----------------------
为了不使bcc的优化起作用,改一下代码:
#include <stdio.h>
void plus(int b);
#ifdef MAIN
int main(void)
{
int a = 0;
plus(a);
printf("a = %d/n", a);
return 0;
}
#endif
void plus(int b)
{
++b;
printf("b = %d/n",b);
}
生成汇编代码:
E:/cl>bcc32 -S -DMAIN tp2.c
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
tp2.c:
打开tp2.asm:
; int main(void)
;
push ebp
mov ebp,esp
push ebx
;
; {
; int a = 0;
;
@1:
xor ebx,ebx ;a =0
;
; plus(a);
;
?live1@32: ; EBX = a
push ebx ;将a的值入栈//注意,不是a的地址
call _plus ;
; ......
_plus proc near
?live1@96:
;
; void plus(int b)
;
push ebp
mov ebp,esp ;
mov eax,dword ptr [ebp+8] ;将a的值给b,即形参与实参结合
; ;dword ptr [ebp+8]就是刚刚push入栈的a
; {
; ++b;
;
?live1@112: ; EAX = b ;EAX就是b
@4:
inc eax ;++b,只改变了局部变b的内容,没有改变a的内容
;
; printf("b = %d/n",b);
;
push eax
push offset s@+8
call _printf
add esp,8
;
; }
;
?live1@144: ;
@5:
pop ebp
ret
_plus endp
再看下边的程序:
#include <stdio.h>
void plus(int *b);
#ifdef MAIN
int main(void)
{
int a = 0;
plus(&a);
printf("a = %d", a);
return 0;
}
#endif
void plus(int *b)
{ ++ *b;
}
; int main(void)
;
push ebp
mov ebp,esp
push ecx
;
; {
; int a = 0;
;
@1:
xor eax,eax ;eax 清0
mov dword ptr [ebp-4],eax ;a = 0,dword ptr [ebp-4]就是局部变量a
;
; plus(&a);
;
lea edx,dword ptr [ebp-4] ;(Load Effective Address)得到a的有效地址,即:&a
push edx ;将a的地址入栈
call _plus
;..............
_plus proc near
?live1@96:
;
; void plus(int *b)
;
push ebp
mov ebp,esp
;
; { ++ *b;
;
@4:
mov eax,dword ptr [ebp+8] ;b = &a;dword ptr [ebp+8]是参数在栈中的地址
inc dword ptr [eax] ;将eax地址处的内容++ (即: ++*b ;++*(&a) )
;
; }
;
@5:
pop ebp
ret
通过这些汇编代码可以看出c参数传递的特性
c 语言中的函数参数
最新推荐文章于 2025-03-28 00:27:41 发布
144

被折叠的 条评论
为什么被折叠?



