《C Primer Plus》学习笔记之 函数
2015-11-05 17:46
357 查看
函数概述
C的设计原则是把函数作为程序的构成模块。函数原型声明(function prototype declaration)
函数原型声明只是将函数类型告诉编译器,并不是函数的实际实现。ANSI C使用函数原型来声明函数的返回值类型、参数个数以及参数类型,通过这种方式,编译器可以检查函数调用语句是否和其原型声明相一致(比如检查参数个数、参数类型是否匹配)。若有参数类型不匹配但都是数值类型,编译器会自动把实际参数转换为和形式参数类型相同的数值。
int imax(int,int); //原型声明中的形参名字可省略,因为编译器并不关心形参的名字 int imax(int a,int b); //原型声明中的形参具有函数原型作用域,不必和函数定义中使用的变量名相匹配。
无参数的函数原型声明
void imax(void); //声明函数既不接受参数也没有返回值 void imax(); //ANSI C编译器会认为未使用函数原型声明该函数,不进行参数检查,应避免
不确定参数的函数原型声明
int printf(char *,...); //表示第一个参数是一个字符串,而其与参数的个数以及类型不能确定
函数定义(function definition)
函数定义是函数的实际实现代码,函数定义的一般格式为:函数类型 函数名(形参类型1 形参1,形参类型2 形参2,...)//ANSI C函数头 { return 表达式;//表达式的数值的类型应与函数头中函数类型相一致 }
从调用函数到被调用函数的通信方法(多输入):
形式参量(formal parameter):函数的输入参数,属于局部变量,为函数私有的,具有代码块作用域注意:ANSI C要求每个形参都要声明其类型,
void dibs(int x,y,z)这种函数头是错误的。
从被调用函数到调用函数的通信方法(单输出):
return语句:通过return语句函数被调用函数可以返回一个值给调用函数,返回的值的类型由函数类型(function type)指定。int imin(int, int); void main(void) { int evil1, evil2; printf("Enter a pair of integers(q to quit):\n"); while(scanf("%d, %d", &evil1, &evil2) == 2) { printf("The lesser of %d and %d is %d.\n", evil1, evil2, imin(evil1, evil2)); printf("Enter a pair of integers(q to quit):\n"); } printf("Bye.\n"); } int imin(int n, int m) { int min; if(n < m) min = n; else min = m; return min;//变量min是imin()函数私有的局部变量,通过return语句把min的数值返回给了调用函数 }
return语句的对象可以是任意表达式,并不仅仅是变量。
int imin(int n, int m) { return (n < m) ? n : m;//返回表达式的结果 }
return语句的另一个作用是终止执行函数,并把控制返回给调用函数的下一个语句,即使return不是函数的最后一个语句。
在函数中多次使用return语句的方式并没有错,但是只在函数结尾使用一次return的方式会使函数的执行流程更清晰。
int imin(int n, int m) { if(n < m) return n;//如果n<m,程序运行到此处就会终止函数的运行,并返回n,后边的代码不会得到执行 else return m; }
若函数没有返回值,函数类型应为void,这种情况下可以没有return语句;如有需要也可以使用
return;语句,该语句会终止执行函数并把控制返回给调用函数的下一个语句,此种形式只能用于void类型的函数中。
另外,函数的返回值不仅可以被赋值给一个变量,也可以被用作表达式的一部分。
answer = 2 * imin(z, zstar) + 25; printf("%d\n", imin(-32 + answer, LIMIT));
函数调用(function call)
实际参数(actual argument)形式参量是被调用函数的局部变量,实际参数是调用函数分配给被调用函数的形式参量的具体值。
实际参数可以是常量、变量或一个复杂的表达式,无论何种形式,函数调用时首先计算其值,然后将该值赋值给被调用函数的形式参量。
函数被调用时,创建声明为形式参量的变量(分配内存),然后用计算后得到的实际参数的值初始化该变量。
#include <stdio.h> #include <string.h> #define NAME "GIGATHINK, INC." #define ADDRESS "101Megabuck Plaza" #define PLACE "Megapolis, CA 94904" #define WIDTH 40 #define SPACE ' ' void show_n_char(char ch, int num); //函数原型声明 void main(void) { int spaces; show_n_char('*',WIDTH); //函数调用,使用常量作为实际参数 putchar('\n'); show_n_char(SPACE,12); 4000 //函数调用,使用常量作为实际参数 printf("%s\n",NAME); spaces = (WIDTH - strlen(ADDRESS))/2; show_n_char(SPACE,spaces); //函数调用,使用变量作为实际参数 printf("%s\n",ADDRESS); show_n_char(SPACE,(WIDTH - strlen(PLACE))/2); //函数调用,使用表达式作为实际参数 printf("%s\n",PLACE); show_n_char('*',WIDTH); //函数调用,使用常量作为实际参数 putchar('\n'); } void show_n_char(char ch, int num) //函数定义,形参ch,num为函数的私有局部变量 { int count; for(count = 1; count <= num; count++) putchar(ch); }
函数的用法
单文件C程序
在单文件C程序中,函数的使用较简单,一般用法是:首先进行函数原型声明(对于非常简单的函数可以在声明的同时进行定义,即用定义作为原型声明)
然后在main函数中对函数进行调用(函数原型声明必须在函数调用之前)
最后进行函数定义
这种结构的好处是程序结构清晰,main函数中只做函数调用,函数的实现都在定义中。
多文件C程序
对于多文件C程序,函数的原型声明、定义和调用可能并不在一个文件中,这时函数的使用就稍微复杂一些,但是函数的使用原则还是相同的,即在函数调用所在的文件中必须先进行函数原型声明。如果file1.c文件中的函数1需要调用file2.c文件中的函数2,首先函数2必须具有外部链接(即原型声明无static修饰或有extern修饰),然后需要在file1.c文件中对函数2进行原型声明,这样就可以在file1.c文件中调用函数2了。
如果程序有很多个文件中的函数都需要调用函数2,则需要在每个调用函数2的文件中都对其进行原型声明,这样很不方便,C提供了一种使用头文件(.h文件)的方法来避免这种复杂的操作。
把具有外部链接的函数原型声明放在头文件中,需要调用该函数的文件在头部使用#include语句包含对应的头文件即可。
而对于具有内部链接的函数,需要在函数定义的源文件中进行函数原型声明,并且必须有static修饰。
头文件中除了进行外部函数的原型声明外,还应该包含用#define语句定义的常量。
函数间的通信
前文中介绍了调用函数向被调用函数传递值以及被调用函数返回一个值的方法。向被调用函数传递值的方式是最简单的,称为值传递,这种方式只是将实际参数的值赋给了形式参量,函数对形式参量的操作不会影响到实际参数。
如果想要在被调用函数中改变调用函数中的变量,则需要用其他的方式。
另外,如果被调用函数需要返回超过一个值,那么return返回值的方法就做不到了,需要用其他方式。
函数间通信的三种方式:
值传递:形参是实参的拷贝。
指针传递:形参是指向实参地址的指针。
引用传递:形参是实参的“别名”。(引用传递是C++的概念,C是没有的)
值传递
值传递就是调用函数通过实际参数把值赋给形式参量,被调用函数中的形式参量是私有的局部变量,对其进行的任何操作都不会改变实际参数。void Exchg1(int x, int y)//形参为int类型变量 { int tmp; tmp = x; x = y; y = tmp; printf("x = %d, y = %d.\n", x, y); } void main(void) { int a = 4, b = 6; Exchg1(a, b);//实参为int类型变量,通过函数调用将实参的值赋值给形参 printf("a = %d, b = %d.\n", a, b); }
程序输出结果
x = 6, y = 4
a = 4, y = 6
main函数调用Exchg1函数时候的隐含操作为:
int x = a;
int y = b;
即创建int类型变量x、y并分别用a、b的值初始化。因此对x、y的任何操作并不会影响到a、b。
指针传递
对形参内容的操作就是对实参本身的操作。void Exchg2(int *px, int *py)//形参为指向int类型的指针变量 { int tmp = *px; *px = *py; *py = tmp; printf("*px = %d, *py = %d.\n", *px, *py); } void main(void) { int a = 4; int b = 6; Exchg2(&a, &b);//实参为int型变量的地址,通过函数调用将实参的地址赋值给形参,即形参指向实参 printf("a = %d, b = %d.\n", a, b); }
main函数调用Exchg2函数时候的隐含操作为:
int *px = &a;
int *py = &b;
即创建指向int类型的指针变量px、py,并分别用a、b的地址初始化,因此px、py分别指向a、b,对*px、*py的操作实际就是对变量a、b本身的操作。
引用传递
void Exchg3(int &x, int &y) // 注意定义处的形式参数的格式与值传递不同 { int tmp = x;x = y; y = tmp; printf("x = %d, y = %d.\n", x, y); } main() { int a = 4; int b = 6; Exchg3(a, b); //注意:这里调用方式与值传递一样 printf("a = %d, b = %d.\n”, a, b); }
引用传递在形式上与值传递唯一区别是在定义的时候有取地址符”&“,x、y相当于a、b的“别名”,对x、y的操作实际就是对a、b本身的操作。
相关文章推荐
- iOS TableView实现QQ好友列表(三)
- 图形学实验Bezier曲线生成
- 练习!!标准体重
- 栈溢出原理与实践
- 最新版SDWebImage的使用
- UIViewController中edgesForExtendedLayout属性的画面切断现象
- zookeeper启动异常
- Android菜单详解——子菜单(SubMenu)
- 画二维图像
- (1)Two Sum--求数组中相加为指定值的两个数
- jQuery wrap wrapAll wrapInner使用
- 使用cookie解决微信不能存储localStorage的问题
- Android-05 Android的MVC设计框架浅析
- Visio 2013绘制时序图
- RSA加密(4.0)
- 水印控件
- 注意:只有xcode5.1创建的项目会自动适配iphone6,iphone6p
- 机器学习复习——Apriori
- 注意:只有xcode5.1创建的项目会自动适配iphone6,iphone6p
- PHP一些验证字符串的函数