C语言学习之六——函数_1

  1. 函数的概述
    一个较大的程序可分为若干个程序模块,每一个模块用来实现一个特定的功能。在高级语言中用子程序实现模块的功能。子程序由函数来完成。一个C程序可由一个主函数和若干个其他函数构成。

    函数的调用关系:由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。如下图所示:


    函数调用关系

//例1
/# include <stdio.h>
void main()
{
void printstar(); /对printstar函数声明/
void print_message(); /对print_message函数声明/
printstar(); /调用printstar函数/
print_message(); /调用print_message函数/
printstar(); /调用printstar函数/
}
void printstar() /定义printstar函数/
{
printf("* * * * * * * * * * * * * * * *\n");
}

void print_message() /定义print_message函数/
{
printf("How do you do!\n");
}
//输出:
/* * * * * * * * * * * * * * * *
How do you do!
/* * * * * * * * * * * * * * * *
(注:定义函数可以在主函数之前定义也可以在主函数之后定义,但是需要注意定义的顺序不同是有差异的。1)在主函数之前定义,则在主函数(或其他函数)调用该函数时不需要再声明,直接调用即可;2)在主函数之后定义,则在主函数(或其他函数)调用该函数时需要声明,再调用,否则会出错。(为了养成良好的书写习惯,在调用函数时都事先进行声明,再调用))

函数的相关说明:1)一个C程序有一个或多个程序模块组成,每个程序模块作为一个源程序文件。、对于较大的程序,一般是将其分割为若干个子程序模块。这样便于分别编写、分别编译,以提高调试效率。(一个源程序文件可以为多个C程序公用)
2)一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为
单位进行编译的。
3)C程序的执行是从main函数开始的,如是在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。
4) 所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义(但是可以嵌套调用)。函数间可以互相调用,但不能调用main函数。main函数是系统调用的。
5)从用户使用的角度看,函数有两种:
(1)标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。应该说明,不同的C系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。
(2)用户自己定义的函数。用以解决用户的专门需要。
6)从函数的形式看,函数分两类:
(1)无参函数。如例1中的printstar()和print_message()就是无参函数。在调用无参函数时,主调函数不向被调用函数传递数据。无参函数一般用来执行指定的一组操作。例如,例1程序中的printstar函数。
(2)有参函数。在调用函数时,主调函数在调用被调用函数时,通过参数向被调用函数传递数据,一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。 如例2

//例2
/#include<stdio.h>
int max(int x, int y) /* 这里就是在函数被调用时传入参数x,y /
{
int z;
z = x > y ? x : y;
return z; //
该函数执行结束后返回z的值 */
}

void main()
{
int max(int x, int y);
int i, j, k;
printf("Please input two numbers: ");
scanf("%d%d", &i, &j);
k = max(i, j);
printf("The maximum number is: %d\n", k);
}
//Please input two numbers: 15 16
The maximum number is: 16

  1. 函数定义的一般形式
    定义无参函数的一般形式:
    类型标识符 函数名() /* 这里的类型标识符即为返回值的类型 /
    {
    声明部分
    语句部分
    }
    定义有参函数的一般形式:
    类型标识符 函数名(形式参数列表) /
    这里的类型标识符即为返回值的类型 */
    {
    声明部分
    语句部分
    }
    定义控函数的一般形式:(注:空函数被调用时什么也不做,其目的是为以后扩展程序功能做准备)
    类型标识符 函数名()
    {

    }

  2. 函数的参数和函数的值
    形参(形式参数):在定义有参函数时,函数名后面括号中的变量名称即为“形式参数”。(注:形参不占内存,只有当调用该函数时,传入实参才开辟内存空间)(注2:在定义形参时需要声明其数据类型,如果没有声明则系统默认为int型。)
    实参(实际参数):在主函数调用一个函数时,函数名后面括弧中的参数(也可以是一个表达式)称为“实际参数”。(注:实参是占内存的)
    在多数情况下,主调函数和被调函数之间室友数据传递关系的。(在不同的函数之间传递数据可以使用:1)参数:通过形参和实参;2)返回值:函数通过return返回计算结果给调用它的函数;3)全局变量)

    形参与实参的说明:
    1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数max(int x, int y)中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。
    2)实参可以是常量、变量或表达式,如:max(3,a+b); 但要求它们有确定的值。在调用时将实参的值赋给形参。
    3)在被定义的函数中,必须指定形参的类型。(注:如果没有制定类型,编译器会warning,但是默认该形参为int型。为了养成良好的书写习惯应该指定形参类型。)
    4)实参与形参的类型应相同或赋值兼容。(如果形参与实参类型不同,则系统自动将实参转换为形参的数据类型。)见例3

//例3
/#include<stdio.h>
int max(int x, int y)
{
int z;
z = x > y ? x : y;
printf("The max number is: %d \n", z);
return z;
}
void main()
{
float i = 15.5, j = 16.5;
printf("The maximum number is: %d\n", max(i, j));
}
//输出: The max number is: 16
The maximum number is: 16

5)在C语言中,实参向对形参的数据传递是“值传递”(相当于COPY),单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数的实参的值。例如,若在执行函数过程中x和y的值变为10和15,而a和b仍为2和3。


值传递
函数的返回值:通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。、
    1)函数的返回值是通过函数中的return语句获得的:return语句将被调用函数中的一个确定值带回主调函数中去。(需要返回值则return,不需要则不用)(一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作用。)
    2)函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。
    3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。(如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。)
    4)对于不带回值的函数,应当用“void”定义函数为“无类型”(或称“空类型”)。(这样,系统就保证不使函数带回任何值,即禁止在调用函数中使用被调用函数的返回值。此时在函数体中不得出现return语句。)
  1. 函数的调用
    • 函数调用一般形式:函数名(实参列表)(注:调用无参函数,则括号为空;如果实参列表包含多个实参,则各参数间用逗号隔开。实参与形参的个数应该相等,类型应匹配。(对实参列表中实参的求值顺序并不是确定的,有的编译器按照自左向右的顺序,有的按照自右向左的顺序。


      函数调用中,实参列表的的求值顺序
  * 函数调用的三种方式:1)函数语句:把函数作为一个语句。有时不需要函数有任何返回值,只要求函数完成一定的操作。
    2)函数表达式:函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值,用以参加表达式的运算。例:c = 2 * max(a, b);
    3)函数参数:函数调用作为一个函数的实参。例如: m = max(a, max(b, c)); 又例:printf(“%d”, max(a, b));
* 对被调用函数的声明和函数原型:
    1)被调用函数必须是一个已经存在的函数。
    2)如果调用库函数(或其他源程序文件中的函数),应该在源程序文件开头用#include命令将调用的有关库函数(或其他源程序文件中的函数)所需要的信息“包含”到本文件中。
    3)如果使用用户自己定义的函数,而该函数的位置与调用它的函数在同一个源程序文件中,则只需要在调用该函数的的函数中做出声明,在调用即可。如例4,

//例4
/#include<stdio.h>
int max(int x, int y)
{
int z;
z = x > y ? x : y;
printf("The max number is: %d \n", z);
return z;
}
void main()
{
int i = 15, j = 16;
printf("The maximum number is: %d\n", max(i, j));
}
//输出: The max number is: 16
The maximum number is: 16

  • 函数的定义与声明:
    函数的声明的作用:是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。(例如函数名是否正确,实参与形参的类型和个数是否一致)(不占内存)
    函数的定义:是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。(占内存)
  1. 函数的嵌套调用
  • C语言中函数不可以嵌套定义,但是可以嵌套调用。(即一个被调用的函数中调用另外一个函数或多个函数。(类似于俄罗斯套娃))。见例5,

//例5
/#include<stdio.h>

int max(int x, int y)
{
int z;
int min(int a, int b);
min(x, y);
z = x > y ? x : y;
printf("The max number is: %d \n", z);
return z;
}
int min(int a, int b)
{
int z;
z = a < b ? a : b;
printf("The min number is: %d\n", z);
}
void main()
{
int i = 15, j = 16;
printf("The maximum number is: %d\n", max(i, j));
}
//输出: The min number is: 15
The max number is: 16
The maximum number is: 16
(这里的函数执行顺序是:1)main函数;2)max函数;3)min函数;4)打印出min函数的值;5)返回max函数的值;6)打印出max函数的返回值;7)程序结束)

  1. 递归函数
    递归函数的定义:函数自身调用自身即为函数的递归(注意:递归函数必须要有一个退出条件,否则将会无限的执行下去。)见例6,例7.

//例6
/#include<stdio.h>
void main()
{
void hanoi(int n, char a, char b, char c);
int n;
char a = '1', b = '2', c = '3';
printf("How many levels of Hanoi do you want?\n");
scanf("%d", &n);
hanoi(n, a, b, c);
}
void hanoi(int n, char a, char b, char c)
{
void move(char i, char j);
if (n == 1) move(a, c);
else{
hanoi(n -1, a, c, b);
move(a, c);
hanoi(n -1, b, a, c);
}
}
void move(char i, char j)
{
printf("%c -> %c\n", i, j);
}
//输出:How many levels of Hanoi do you want?
//输入:3
//输出: 1 -> 3
1 -> 2
3 -> 2
1 -> 3
2 -> 1
2 -> 3
1 -> 3
//分析:(1) 将A上n-1个盘借助C座先移到B座上。
(2) 把A座上剩下的一个盘移到C座上。
(3) 将n-1个盘从B座借助于A座移到C座上。

//例7
/#include<stdio.h>
void main()
{
int i, j = 1, k;
int factorial(int x, int y);
printf("Please input a number: \n");
scanf("%d", &i);
k = factorial(i, j);
printf("The final number is: %d\n", k);
}
int factorial(int x, int y)
{
if (x < 1)
return y;
y = y * x;
x--;
factorial(x, y);
}
//输出:Please input a number:
输入:5
输出:The final number is: 120

  1. 数组作为函数参数
    数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式:
    1)把数组元素作为实参使用; (数组元素与普通变量并无区别。因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。)
    2)把数组名作为函数的形参和实参使用。
    用数组名作函数参数与用数组元素作实参有几点不同:(1)用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。然而,用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。
    (2)在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。
    然而,在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。而是把数组的首地址赋给形参数组名,因此实参与形参共用同一内存空间。(数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存间。)见例8。

//例8
/#include<stdio.h>
void main()
{
void list2(int a[10]);
int a[10] = {1, 2, 3, 4, -1, -2, -3, -4, 2, 3};
list2(a);
}

void list2(int a[10])
{
int i;
for(i = 0; i <= 9; i++)
{
if (a[i] < 0) continue;
else printf("The positive number in array is: %d \n", a[i]);
}
}
//输出:The positive number in array is: 1
The positive number in array is: 2
The positive number in array is: 3
The positive number in array is: 4
The positive number in array is: 2
The positive number in array is: 3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容