当前位置:网站首页>C语言程序设计笔记(浙大翁恺版) 第七章:函数
C语言程序设计笔记(浙大翁恺版) 第七章:函数
2022-08-09 14:23:00 【CS_Lee_】
按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源。原课程链接如下:
https://www.icourse163.org/course/ZJU-9001
由于是大三抽空回头整理的,所以可能前五章会记的内容比较简略。此外,作为选学内容的A0:ACLLib的基本图形函数和链表两章也没有做。西电的考试是机试,理论上学到结构体就能够应付考试了,但为了以后的学习考虑建议全学。
其他各章节的链接如下:
函数
函数的定义和使用
函数的定义和调用
什么是函数?
函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值
可以先想象成数学中的函数: y = f ( x ) y=f(x) y=f(x)
函数定义

调用函数
函数名(参数值);
()起到了表示函数调用的重要作用,即使没有参数也需要()
如果有参数,则需要给出正确的数量和顺序,这些值会被按照顺序依次用来初始化函数中的参数
示例:
#include <stdio.h>
void cheer()
{
printf("cheer\n");
}
int main()
{
cheer;
cheer();
return 0;
}

cheer没有进行函数调用,编译时会警告表达式cheer没有被使用
示例2:
#include <stdio.h>
void sum(int begin, int end)
{
int i;
int sum = 0;
for ( i=begin; i<=end; i++ ) {
sum += i;
}
printf("%d到%d的和是%d\n", begin, end, sum);
}
int main()
{
sum(1,10);
sum(20,30);
sum(35,45);
return 0;
}
1到10的和是55
20到30的和是275
35到45的和是440
在函数调用sum(1,10)处设置断点,运行到该语句之前会暂停。此时如果还用“下一步”就直接完成sum函数的运行到下一行语句,并不会进入到函数调用,想要程序离开main进入到sum函数内需要用“单步进入”
如果不想循环多遍,可以直接在循环后设一个断点然后用“跳过”

函数返回
函数知道每一次是哪里调用它,会返回到正确的地方
从函数中返回
从函数中返回值
return停止函数的执行,并送回一个值
return有两种写法:return;和return 表达式;
一个函数里可以出现多个return语句,这些return不一定要放在最后。但这样不符合单一出口的理念
return的值可以赋值给变量,可以再作为参数传递给函数,甚至可以丢弃而不会有任何警告
示例:
#include <stdio.h>
int max(int a, int b)
{
int ret;
if ( a>b ) {
ret = a;
} else {
ret = b;
}
return ret;
}
int main()
{
int a,b,c;
a = 5;
b = 6;
c = max(10,12);
c = max(a,b);
c = max(c, 23);
c = max(max(23,45), a);
c = max(23+45, b);
max(23,45);
printf("%d\n", max(a,b));
return 0;
}
在printf处单步进入会进入max,因为在printf之前要先把传给printf的参数都准备好
没有返回值的函数
void 函数名(参数表)
不能使用带值的return,可以没有return,函数运行到最后一行就会自行返回
调用的时候不能做返回值的赋值。如果函数有返回值,就必须使用带值的return
函数的参数和变量
函数原型
用来告诉编译器这个函数长什么样
函数先后关系
像之前这样把sum()写在上面,是因为C的编译器自上而下顺序分析你的代码,在看到sum(1,10)的时候,它需要知道sum()的样子,也就是sum()要几个参数,每个参数的类型如何,返回什么类型,这样它才能检查你对sum()的调用是否正确
如果不知道,也就是把要调用的函数放到下面了,LLVM编译时会先警告然后报错。在C99以前,C的编译器不知道函数的样子会猜函数长什么样,但当遇到实际的sum()函数定义时发现与猜的不匹配就会报错
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YlyeximZ-1659884485854)(C语言程序设计.assets/image-20220725160920522.png)]](/img/8c/4ac95bde55bc5ace0d1f8f4ac23b73.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kVDk3z6Y-1659884485856)(C语言程序设计.assets/image-20220725161720044.png)]](/img/81/129acd3f42e7cc7e4b4047e2b63114.png)
将函数头拷贝到main函数前加分号作为函数原型声明,函数定义放在下面以通过编译
有了声明之后,编译器就知道函数长什么样子,根据原型判断对函数的调用是否正确,确保函数定义和声明是否一致
函数原型
函数头,以分号“;”结尾,就构成了函数的原型
函数原型的目的是告诉编译器这个函数长什么样(名称、参数数量及类型、返回类型)
原型里可以不写参数的名称,编译器检查时不会检查参数名称,但是一般仍然写上
示例:
double max(double a, double b);
int main()
{
int a,b,c;
a = 5;
b = 6;
c = max(10,12);
printf("%d\n", c);
return 0;
}
double max(double a, double b)
{
// ...
}
会发生自动类型转换,将12转换为double交给a
参数传递
调用函数的时候,是用表达式的值来初始化函数的参数
如果函数有参数,调用函数时必须传递给它数量、类型正确的值
可以传递给函数的值是表达式的结果,这包括:字面量、变量、函数的返回值、计算的结果
调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞,编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的。后续的语言,C++/Java在这方面很严格
示例:
#include <stdio.h>
void cheer(int i)
{
printf("cheer %d\n", i);
}
int main()
{
cheer(2.4);
return 0;
}

不同编译器不一定会出现警告,如果改为cheer(2.0),课程中使用的编译器就不出现警告
C语言在调用函数时,永远只能传值给函数
示例:
void swap(int a, int b);
int main()
{
int a = 5;
int b = 6;
swap(a,b);
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
swap和main里的a和b存在不同的地方,是没有任何联系的变量,不能实现交换a和b的值
传值
每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
示例:
在main中,swap里的x和y不存在。同样在swap中,main里的a和b也不存在
Dev C++中变量不存在会提示“Not found in current context”,如果存在会给出值
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy7f8OZB-1659884485859)(C语言程序设计.assets/image-20220725182557289.png)]](/img/2a/109a57ce4a1d88d18da632c5532eb5.png)
过去,对于包括原型声明和函数定义的函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫作“实际参数”
由于容易让初学者误会实际参数就是实际在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去了,所以我们不建议继续用这种古老的方式来称呼它们。我们认为,它们是参数和值的关系
本地变量
定义在函数内部的变量是本地变量(局部变量,自动变量),参数也是本地变量
本地变量
函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
定义在函数内部的变量就是本地变量
参数也是本地变量
变量的生存期和作用域
生存期:什么时候这个变量开始出现了,到什么时候它消亡了
作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,这两个问题的答案是统一的:大括号内 —— 块
示例:

当离开main函数调用swap函数时会离开main的变量空间进入swap的变量空间。离开a和b的作用域,不能在当前上下文访问a和b,但作为生存来说a和b还存在
swap函数运行完返回后swap的变量空间就没有了,x,y和t不存在
本地变量的规则
本地变量是定义在块内的。它可以定义在函数的块内,也可以定义在语句的块内,甚至可以随意拉一对大括号来定义变量
程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
块外面定义的变量在里面仍然有效,块里面定义了和外面同名的变量则掩盖了外面的
不能在一个块内定义同名的变量
本地变量不会被默认初始化,参数在进入函数的时候被初始化了
示例:
void swap(int a, int b);
int main()
{
int a = 5;
int b = 6;
swap(a,b);
if ( a<b ) {
int i = 10;
}
i++;
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}

编译报错说i未声明。i是定义在块内的变量,它的生存期和作用域就仅限于这个块,在这个块内i存在,离开就不存在了。程序的每次运行i是否存在取决于a和b的大小关系
在对i进行初始化之前,i已经存在
示例2:
void swap(int a, int b);
int main()
{
int a = 5;
int b = 6;
swap(a,b);
{
int i = 0;
printf("%d\n",i);
}
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
0
a=5,b=6
这对大括号没有依附于任何语句,在块内i可以参与运算和输出
如果将printf("%d\n",i)改为printf("%d\n",a),得5
示例3:
void swap(int a, int b);
int main()
{
int a = 5;
int b = 6;
swap(a,b);
{
int a = 0;
printf("a=%d\n",a);
}
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
a=0
a=5,b=6
其他细节
main是什么?
没有参数时
当函数没有参数时,是在参数表里加void写成void f(void);还是void f();?
前者明确告诉编译器该函数不接收任何参数,后者在传统C中,它表示f函数的参数表未知,并不表示没有参数
示例:
// void swap(int a, int b);
void swap();
int main()
{
int a = 5;
int b = 6;
swap(a,b);
{
int a = 0;
printf("a=%d\n",a);
}
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
编译通过。这样写告知编译器只知道有swap,但不确定swap的参数,编译器遇到swap(a,b);会猜测swap的参数是两个int
如果将void swap(int a, int b)改为void swap(double a, double b),并在int t = a后添加printf("in swap,a=%f,b=%f\n", a, b),得

实际上swap的参数是两个double,但编译器认为swap的参数是两个int,为swap函数调用安排两个int的传递就会出错
调用函数时的逗号和逗号运算符怎么区分?
调用函数时的圆括号里的逗号是标点符号,不是运算符
示例:
f(a,b)传两个参数
f((a,b))再加一层括号表明要先做括号里的运算,这时,就是逗号运算符,只传一个参数
C语言不允许函数嵌套定义
可以在一个函数里放另一个函数的声明,但是不能放一个函数的定义
int i,j,sum(int a, int b);定义int型变量i和j,声明sum函数需要两个int作为参数并且返回一个int
return (i);的圆括号没有意义
关于main
int main()也是一个函数,如果main不需要任何参数,可以写成int main(void)
main是C语言程序的入口,但main也是一个函数。main成为C语言的入口函数其实和C语言本身无关,它虽然是你写的代码当中第一个被执行的地方,但并不是程序运行起来第一条运行的代码,你的代码是被一小段叫做启动代码的程序所调用的,它在main函数之前会为程序的运行做准备,做完准备工作后再调用main函数
操作系统把你的可执行程序装载到内存里,启动运行执行编译器给你的启动代码,启动代码里会寻找main函数并执行,所以main才成为启动代码的入口函数。如果编译器采用另外的方式来编译程序,启动代码就可能不是去寻找main了,如对于Win32API,这时要寻找的就是WinMain
return 0是有意义的,main函数结束时会把0返回给调用它的地方,返回给的一小段代码会检查main返回什么,然后报告给操作系统
传统上一个程序返回0表示正常运行结束,返回任何非零值表示在程序运行过程当中出现错误
示例:
如果在Windows的批处理(.bat)文件调用你编写的程序,然后跟上一句if errorlevel 1 ...,如果程序返回1就会做相关处理
对于Unix,如果使用的是bashell,可以使用echo $?查看程序返回的结果

边栏推荐
猜你喜欢

RHCE Course Summary

YOLOv5网络详解

Shell course summary

Refuse to "reinvent the wheel", Baidu EasyDL lets you play with AI custom development

table中 You may have an infinite update loop in a component render function问题解决

R7 6800H标压处理器+RTX 3050独显 无畏Pro15锐龙版高能开卖

*2-1 OJ 254 Flip Pancakes

*5-1 CCF 2015-03-1 Image rotation

IK学习笔记(1)——CCD IK

一款翻译机背后的全球经济浪潮
随机推荐
在Word中如何调整编号和文字之间的间距?
shell提取ip地址
同事的接口文档我每次看着就头大,毛病是真的多多多。。。
VMWare does not use easy install, install ISO manual manually
运维--常用中间件
MySQL lock mechanism and lock algorithm
【DevOps】jekins配置(二)
*4-1 CCF 2014-12-1 Access Control System
leetcode 剑指 Offer 07. 重建二叉树
汉源高科百兆2光3电4电光纤级联型光电转换器工业矿用本安型光纤收发器迷你嵌入式主板
【Database】Sqlserver如何定时备份数据库和定时清除
IK学习笔记(1)——CCD IK
Computational Imaging Technology
*3-2 CCF 2014-09-2 drawing
Word numbering and text spacing are too large
Shell course summary
[manjaro] updated kernel file loading failure
RHCE Course Summary
dpkg: error: cannot new file '/var/lib/dpkg/info/format-new': no
同步锁synchronized追本溯源