一、函数的分类

前面已经说过,C语言中的函数就是面向对象中的"方法",C语言的函数可以大概分为3类:

1.主函数,也就是main函数。每个程序中只能有一个、也必须有一个主函数。无论主函数写在什么位置,C程序总是从主函数开始执行

2.开发人员自定义的函数,可有可无,数目不限

3.C语言提供的库函数,例如stdio.h中的输出函数printf()和输入函数scanf()

虽说C中的函数类似于Java中的方法,但在使用上还是有区别的。

1.在Java中,每个方法的定义顺序没有限制,在前面定义的方法内部可以调用后面定义的方法

1 public void test() {
2 int c = sum(1, 4);
3 }
4
5 public int sum(int a, int b) {
6 return a + b;
7 }

第1行定义的test方法可以调用在第5行定义的sum方法

2.在标准C语言中,函数的定义顺序是有讲究的,默认情况下,只有后面定义的函数才可以调用前面定义过的函数

1 int sum(int a, int b) {
2 return a + b;
3 }
4
5 int main()
6 {
7 int c = sum(1, 4);
8 return 0;
9 }

第5行定义的main函数调用了第1行的sum函数,这是合法的。如果调换下sum函数和main函数的顺序,在标准的C编译器环境下是不合法的(不过在Xcode中只是警告,Xcode中用的是GCC编译器)

3.如果想把其他函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数前面作一下函数的声明

 1 // 只是做个函数声明,并不用实现
2 int sum(int a, int b);
3
4 int main()
5 {
6 int c = sum(1, 4);
7 return 0;
8 }
9
10 // 函数的定义(实现)
11 int sum(int a, int b) {
12 return a + b;
13 }

我们在第2行做了sum函数的声明,然后在第6行(main函数中)就可以正常调用sum函数了。

函数的声明格式:

返回值类型  函数名 (参数1, 参数2, ...)

可以省略参数名称,比如上面的sum函数声明可以写成这样:

int sum(int, int);

只要你在main函数前面声明过一个函数,main函数就知道这个函数的存在,就可以调用这个函数。究竟这个函数是做什么用,还要看函数的定义。如果只有函数的声明,而没有函数的定义,那么程序将会在链接时出错。

4.在大型的C程序中,为了分模块进行开发,一般会将函数的声明和定义(即实现)分别放在2个文件中,函数声明放在.h头文件中,函数定义放在.c源文件中

下面我们将sum函数的声明和定义分别放在sum.h和sum.c中

sum.h文件

sum.c文件

然后在main.c中包含sum.h即可使用sum函数

其实sum.h和sum.c的文件名不一样要相同,可以随便写,只要文件名是合法的

运行步骤分析:

1> 在编译之前,预编译器会将sum.h文件中的内容拷贝到main.c中

2> 接着编译main.c和sum.c两个源文件,生成目标文件main.obj和sum.obj,这2个文件是不能被单独执行的,原因很简单:

* sum.obj中不存在main函数,肯定不可以被执行

* main.obj中虽然有main函数,但是它在main函数中调用了一个sum函数,而sum函数的定义却存在于sum.obj中,因此main.obj依赖于sum.obj

3> 把main.obj、sum.obj链接在一起,生成可执行文件

4> 运行程序

说到这里,有人可能有疑惑:可不可以在main.c中包含sum.c文件,不要sum.h文件了?

大家都知道#include的功能是拷贝内容,因此上面的代码等效于:

这么一看,语法上是绝对没有问题的,但是绝对运行不起来,在链接时会出错。原因:编译器会编译所有的.c源文件,这里包括main.c、sum.c,编译成功后生成sum.obj、main.obj文件,当链接这两个文件时链接器会发现sum.obj和main.obj里面都有sum函数的定义,于是报"标识符重复"的错误。

有人可能觉得分出sum.h和sum.c文件的这种做法好傻B,好端端多出2个文件,你把所有的东西都写到main.c不就可以了么?

  • 没错,整个C程序的代码是可以都写在main.c中。但是,如果项目做得很大,你可以想象得到,main.c这个文件会有多么庞大,会严重降低开发和调试效率。
  • 要想出色地完成一个大项目,需要一个团队的合作,不是一个人就可以搞的定的。如果把所有的代码都写在main.c中,那就导致代码冲突,因为整个团队的开发人员都在修改main.c文件,张三修改的代码很有可能会抹掉李四之前添加的代码。
  • 正常的模式应该是这样:假设张三负责编写 main函数,李四负责编写一系列的自定义函数,张三需要用到李四编写的某个函数,怎么办呢?李四可以将所有的函数声明在一个.h文件中,比如 lisi.h,然后张三在他自己的代码中包含lisi.h文件,接着就可以调用lisi.h中声明的函数了,而李四呢,可以独立地在另外一个文件中(比如lisi.c)编写函数的定义,实现那些在lisi.h中声明的函数。这样子,张三和李四就可以相互协作、不会冲突。

三、函数的形参和实参

在定义函数时,函数名后面的()中定义的变量称为形式参数(形参);在调用函数时传入的值称为实际参数(实参)。

// b是test函数的形参(形式参数)
void test(int b)
{
b = 9; // 改变了形参b的值
} int main()
{
int a = 10;
printf("函数调用前的a:%d\n", a); test(a); // a是test函数的实参(实际参数) printf("函数调用后的a:%d", a);
return 0;
}

如果是基本数据类型作为函数的形参,那是简单的值传递,将实参a的值赋值给了形参b,相当于

int a = 10;
int b = a;
b = 9;

a和b是分别有着不同内存地址的2个变量,因此改变了形参b的值,并不会影响实参a的值。

上述代码的输出结果为:

【C语言】01-函数的更多相关文章

  1. C 语言 时间函数使用技巧(汇总)

    time.h 头文件 是 C 语言中 有关 时间的函数所储存的头文件 #include <time.h> 在介绍时间函数用法之前,我们首先要了解在 time.h 头文件中已经声明了的一个结 ...

  2. C语言pow函数编写

    C语言pow函数编写 #include<stdio.h> double chaoba(double f,double q); //声明自定义函数 void main(void) { dou ...

  3. C语言-自定义函数

    C语言自定义函数 --1-- 自定义函数定义 1.1 无参无返回值函数 1.2 无参有返回值函数 1.3 有参无返回值函数 1.4 有参有返回值函数 --2-- 函数的参数 2.1 形式参数介绍和使用 ...

  4. C语言printf()函数:格式化输出函数

    C语言printf()函数:格式化输出函数 头文件:#include <stdio.h> printf()函数是最常用的格式化输出函数,其原型为:     int printf( char ...

  5. C语言的函数

    "函数"在英文的翻译是"function",无论在自然科学还是计算机科学都是这个词,而"function"的本意是"功能" ...

  6. c语言main函数返回值、参数详解(返回值是必须的,0表示正常退出)

    C语言Main函数返回值 main函数的返回值,用于说明程序的退出状态.如果返回0,则代表程序正常退出:返回其它数字的含义则由系统决定.通常,返回非零代表程序异常退出. 很多人甚至市面上的一些书籍,都 ...

  7. Go语言示例-函数返回多个值

    Go语言中函数可以返回多个值,这和其它编程语言有很大的不同.对于有其它语言编程经验的人来说,最大的障碍不是学习这个特性,而是很难想到去使用这个特性. 简单如交换两个数值的例子: package mai ...

  8. 【学习笔记】【C语言】函数

    一. 什么是函数 任何一个C语言程序都是由一个或者多个程序段(小程序)构成的,每个程序段都有自己的功能,我们一般称这些程序段为“函数”.所以,你可以说C语言程序是由函数构成的. 比如你用C语言编写了一 ...

  9. 【转载】 c语言inline函数的使用

    c语言inline函数的使用 转载自:http://blog.chinaunix.net/uid-21843265-id-3056446.html 大学在教科书上学习过inline函数,定义为inli ...

  10. 【C语言】函数和自定义函数

    函数,我之前也提到过一点点内容.其实函数是很好理解的,但是写起来又十分麻烦. 一.     函数引入 我们知道,C源程序是由函数组成的.请看下面的简单函数例子 #include <stdio.h ...

随机推荐

  1. 派遣例程与IRP结构

    提到派遣例程,必须理解IRP(I/O Request Package),即"输入/输出请求包"这个重要数据结构的概念.Ring3通过DeviceIoControl等函数向驱动发出I ...

  2. wuzhicms短信API 实例调用

    1.接口调用 $sendsms = load_class('sms','sms'); echo $sendsms->send_sms('18911549611', '888888', 1); / ...

  3. 使用list和tuple

    list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> ...

  4. hdoj 1859 最小长方形

    最小长方形 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  5. [OC Foundation框架 - 7] NSArray的创建与遍历

    NSArray是不可变的,不能先创建再添加元素 NSArray可以放入任何OC对象,但不能放入基本数据类型.结构体.枚举等非OC对象 不能存储nil   A.常用方法1 创建 返回用量 是否含有某元素 ...

  6. activemq重启

    172.16.10.72 activemq /home/local/apache-activemq-5.10.0/bin/activemq-admin stop /home/local/apache- ...

  7. Partition Array

    Given an array nums of integers and an int k, partition the array (i.e move the elements in "nu ...

  8. Android设计模式系列--原型模式

    CV一族,应该很容易理解原型模式的原理,复制,粘贴完后看具体情况是否修改,其实这就是原型模式.从java的角度看,一般使用原型模式有个明显的特点,就是实现cloneable的clone()方法.原型模 ...

  9. Project Management - 2) Estimate Your Work

    1. Delphi估算 1) 项目经理召集小组成员,就项目有关事宜向大家进行阐述. 2)大家提出各种问题,然后进行分配任务. 每个人写下自己的任务列表和时间估算. 3)项目经理收集好任务之后进行复查, ...

  10. Graph(2014辽宁ACM省赛)

    问题 F: Graph 时间限制: 1 Sec  内存限制: 128 MB 提交: 30  解决: 5 [cid=1073&pid=5&langmask=0" style=& ...