很久以前写的旧文,不改了,直接发出来 --------------- 这个帖子分以下几个部分: 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参数传递的特性