函数的定义
- 文件顶部要先函数声明,之后才写函数体;(函数声明后面要写分号)【不写函数声明也是可以正常运行的】
- 参数:形参有两种方式:传值型:void fun(int a)、引用型:void fun(int &a) 这两种参数的调用都是 fun(a) 注:引用型是c++语法且不能传常量;形参定义和调用函数实参之间均用‘,’(其实当函数定义无参数时是应该注明 void 的;例如:int fun(void){...});
- 返回值:C语言默认返回值类型为 ' int ' ;空返回类型用 ' void ',就算空返回类型时,函数也可以通过 ‘return ; ’ 结束函数
#include <stdio.h> //函数省略了返回类型:默认int型返回值 test0() { return 123; //一个较小的整数 } test1() { return 4.765f; //浮点数 } test2() { return 12345678;//较大的整数,超过0xFFFF } test3() { return 98765432112345LL;//大整数,超过32位无符号整数的范围 } test4() { printf("In test4.\n");//没有写返回值 } void main() { printf("test0 returns %d\n", test0()); //123 printf("test1 returns %.2f\n", (float)test1()); //4.00 printf("test2 returns %d\n", test2()); //12345678 printf("test3 returns %d\n", test3()); //溢出了 printf("test4 returns %d\n", test4()); //0 }
- 要注意形参是否为指针,若为指针实参要传址,否则只需传值既可以。
- 数组作为实参时:由于数组名是一个地址信息就为地址,所以是传址操作。
- 数组作为形参时:占用指针类型相同的字节(实际上就是一个地址);而实参则不同,传递数组应有大小字节。
void fun(char a[][3],int b[]){ printf("fun形参a:长度为%d,尺寸为%d\n",strlen(a),sizeof(a)); printf("fun形参b:长度为%d,尺寸为%d",strlen(b),sizeof(b)); //从函数中数组形参的尺寸都是8,可以断定其实际为指针 //因为数组形参是指针,所以一维数组可以不写长度 //注:二维数组可以不写行数,但列数必须写 } int main() { char a[][3]={"aaa","bbb","ccc","ddd"}; int b[100]={1,2,3}; printf("main实参a:长度为%d,尺寸为%d\n",strlen(a),sizeof(a)); printf("main实参b:长度为%d,尺寸为%d\n",strlen(b),sizeof(b)); fun(a,b); } //main实参a:长度为13,尺寸为12 //main实参b:长度为1,尺寸为400 //fun实参a:长度为13,尺寸为8 //fun实参b:长度为1,尺寸为8
- 实参字符串数组,相应形参用二级指针接收
void fun(char **p){ //二级指针:指向一级指针,这里指向指针数组 printf("%s %s\n",*(p+1),*p+1); //一次解引用是字符地址(字符串的首地址) printf("%c %c\n",*(*(p+2)+1),1+*(*(p+2)+1)); //两次解引用是某个字符,再加减就是对字符操作了 } int main(){ char *str[]={"asdf","qwer","zxvc"}; fun(str); } //输出:qwer sdf // x y
- 实参是二维数组时,形参的定义定义方式
//形参接收二维数组有两种方法:二维数组、数组指针 //并且两种方式都必须写明列数:系统按列数划分一维数组大小 void fun(char a[][4],char (*b)[2]){ int i,j; printf("二维数组形参(3*4):"); for(i=0;i<3;i++){ for(j=0;j<4;j++) printf("%c",a[i][j]); putchar(' '); }putchar('\n'); printf("二维数组形参(6*2):"); for(i=0;i<6;i++){ for(j=0;j<2;j++) printf("%c",b[i][j]); putchar(' '); } } int main() { int i,j; char ss[][3]={"aaa","bbb","ccc","ddd"}; printf("二维数组形参(4*3):"); for(i=0;i<4;i++){ for(j=0;j<3;j++) printf("%c",ss[i][j]); putchar(' '); }putchar('\n'); fun(ss,ss); } //二维数组形参(4*3):aaa bbb ccc ddd //二维数组形参(3*4):aaab bbcc cddd //二维数组形参(6*2):aa ab bb cc cd dd
- 可变参数的定义
//可变参数:n表示参数的个数;va_list vap为定义一个参数列表;va_start(vap,n),va_arg(vap,int),va_end(vap)为宏定义;(#include <stdarg.h>) #include <stdio.h> #include <stdarg.h> int sun(int n,...); int sun(int n,...){ int i,sum=0; va_list vap; va_start(vap,n); for(i=0;i<n;i++){ sum+=va_arg(vap,int); } va_end(vap); return sum; }
指针函数:定义时要用指针定义
- char *getWord(char);这里为函数定义;因为'()'的优先级高于'*';所以定义的是一个函数,去掉函数名和函数参数结构是返回类型,此处为 char 型指针
//声明:char *getWord(char); char *getWord(char c){ return "abc"; } //上述的函数返回一个字符串:实际返回的为 'a' 的地址;C语言中没有定义字符串类型,只是用char类型的指针来指向字符串,以'\0'结束) 直接返回字符串,c语言则会找到一个固定的存储区域来存放这个常量; //不能返回局部变量的地址的,因为在函数中的局部变量出了函数就已经被销毁了。如:以下就会异常 char *getWord(char c){ char str[]="abc"; return str; }
函数指针:指向函数的指针,函数名就是函数的地址
- int (*p)();函数指针定义:因为'()'的优先级高于'*';所以定义的是一个指针,去掉指针标志 '*' ,剩下的表示指针指向的类型,此处为一空参数且返回值是 int 型的函数;故,函数指针
- 函数指针作为参数:
int add(int ,int); int calc(int (*fp)(int,int),int ,int); //fp为函数指针类型参数的参数名;指针参数也可以不写参数名:int calc(int (*)(int,int),int ,int); int add(int num1,int num2){ return num1+num2; } int calc(int (*fp)(int,int),int num1,int num2){ return (*fp)(num1,num2); //return fp(num1,num2);一样可以运行,相当于间接调用函数 } int main(){ printf("3+5=%d\n",calc(add,3,5)); return 0; } //add为上面定义的一个函数;这里作为参数传入calc函数
- 函数指针作为返回值:
- int ( *select(char) )(int,int);因为'()'的优先级高于'*';所以定义看第一个括号中内容,是一个参数类型为 char 返回值为指针的函数;去掉函数名 select 和参数 char 就剩下这个函数返回值类型(函数指针:指向参数是 (int,int)且返回值是 int 的函数)
#include <stdio.h> int (*select(char))(int ,int); //声明时将函数名定义合并入返回类型中;所以先是select(char)的内容表示一个叫'select'函数,参数列表为char; //然后去掉函数的定义部分就是返回值类型 int (*)(int,int);由此刻得处返回值是一个函数指针; int add(int a,int b){return a+b;} int sub(int a,int b){return a-b;} int (*select(char op))(int,int){ switch(op){ case '+':return add; case '-':return sub; } } int calc(int (*fp)(),int a,int b){ return fp(a,b); } int main(){ char op='-'; int (*fp)(); fp=select(op); printf("3%c5=%d\n",op,calc(fp,3,5)); }
作用域
- 代码块作用域:位于一个 '{}' 中;
- 文件作用域:在代码块之外声明,作用域为从声明开始到文件结尾处均可访问;
- 原型作用域:在函数原型中声明的参数名;
- 函数作用域;
链接属性
- external:声明(允许跨文件访问,默认选项),多个文件中声明的同名标识符表示同一个实体
- internal:单个文件中声明的同名标识符表示同一个实体
- none(无):声明的同名标识符被当做独立不同的实体
- static:修饰的变量或函数只能在本文件中使用,并且会自动赋值为0,在其他文件中链接不过去的。该属性可使原先拥有external属性的标识符变为internal属性。连接属性只能修改一次,只针对具有文件作用域的标识符;
注:对于拥有其他作用域的标识符(变量)是另一种功能——静态:具有静态存储期,(生存期与全局变量相同,但作用域不会改变)
变量的生存期
- 静态存储期:具有文件作用域的变量(函数,全局变量);在程序执行期间将一直占据存储空间,直到程序关闭才释放。
- 自动存储期:具有代码块作用域的变量(即使是函数中的定义的变量都是自动存储期);在代码块结束时将自动释放存储空间。
存储类型
- auto:自动变量,在代码块中声明的变量默认就是 auto。
- register:寄存器变量,将使用频繁的局部变量存储在CPU的寄存器中,由于寄存器中存取速度远高于内存,因此可以提高执行效率
- 这种变量有可能被存储在CPU的寄存器中,所以无法通过取址操作符获得该变量的地址;
- 很多方面和自动变量相同,拥有代码块作用域,自动存储期和空链接属性。
- static:1、修饰有文件作用域的标识符(函数,全局变量):使原先拥有external属性的标识符变为internal属性。
2、修饰无文件作用域的标识符(局部变量):使局部变量指定为静态局部变量,自动赋值为0,具有静态存储期,(生存期与全局变量相同,但作用域不会改变)
注:修饰文件作用域,限定只可在本文件中使用;修饰局部变量,作用域是不变;
- extern:声明这个变量在还未编译的其他位置定义,不要报错;经常用于两源文件使用同一各变量的情况(方便阅读)。
结:auto,register属于自由存储期;static,extern,typedef属于静态存储期;
浙公网安备 33010602011771号