Summary
1)C语言中的函数主要有2种类型:
Function
:数据处理(数据 --> 数据),通过某种规则由输入的数据得到输出数据Procedure
:过程定义(数据 --> 功能),(根据数据)执行一系列动作,进而完成某种功能
2)应用程序必须依靠操作系统才能运行,应用程序接受操作系统的管理。当操作系统运行程序时,首先调用的就是main()函数
。)(main()函数一般是第一个被调用的函数,也有其他方法可以让其他函数先于main执行)
3)应用程序执行的过程:(后续补上时序图)
- 用户双击.exe,或者在命令行中直接运行.exe
- OS把应用程序加载到内存中:Load()
- OS找到应用程序的main()函数
- 执行main()函数
- 关闭应用程序时,
main()函数执行结束
,返回一个值给操作系统
(在windows中,可以在命令行,使用echo %errorlevel%
命令查看刚刚命令执行的exe的返回值)
4) 函数的调用过程:
- 暂停主调函数
- 跳转到被调函数,执行被调“函数体”
- 被调函数返回,恢复执行主调函数
5)工具包就是一个函数集
,包含了一系列定义好的函数。#inlude语句用于声明,要使用工具包中的函数
。
6)void是C语言中的一个基本类型,但void不是基础数据类型,不可以用来定义具体变量
。
7)C语言中如果想定义一个无参函数,必须使用void声明!如果函数参数列表什么都不写,表示接受任意多的参数。即:void f(void) --> 不接受参数
;void f() --> 几个参数都可以
;
8)对于void f()无返回值函数,可以直接使用 return;
语句使函数直接返回
9)形参
:函数定义时的参数列表;实参
:函数调用时指定的具体值;实参用来初始化
形参(将实参的值赋值
给形参),所以形参相当于一个函数内部的变量
(函数参数的本质是变量
)
10)C语言中,当数组作为参数时,数组参数会退化为指针
,数组大小无法传递;此时修改数组形参,会同时改变数组实参的值
。
11)排序的关键操作:比较
和交换
12)全局变量不同同名,会产生命名冲突,报重定义的错误;就近原则
,存在多个同名变量时,优先使用最近定义的变量。
13)变量的作用域指的是变量定义后的可访问范围
;在重叠作用域中,优先使用最近定义的
变量。
14)代码块
指的是从 {
开始到 }
结束的一段代码;局部变量的作用域
从定义开始,到当前代码块结束;
15)对于全局变量,存在两个作用域全局作用域
和文件作用域
。全局作用域指的是在程序的各个角落都可以访问到;文件作用域则只能在当前代码文件中访问并使用;
16)计算机中,物理内存被分为不同区域,不同区域有不同的用途。全局数据区
用于存放全局变量和静态变量;栈
区用于存放局部变量和函数参数;堆
空间用于动态创建变量;
17)生命期
:变量从创建到销毁的时间(合法可用的时间)。全局数据区的变量
:生命期从程序开始到程序结束;栈区的变量
:生命期从进入作用域开始,到作用域结束;
18)作用域和生命期没有本质联系:作用域是语法层面上
对变量是否可访问的规定(空间上
);生命期是二进制层面上
变量存在于内存中的时间(时间上
)
19)static修饰的变量位于全局数据区
;static修饰的全局变量只有文件作用域
;static局部变量只会初始化一次
,作用域仍然是所在代码块。
20)变量的生命期由存储位置决定
。如果变量未初始化
,存储位置决定了变量的初始值
是多少:全局数据区的变量如果未初始,值为0(.bss段);栈区的变量如果未初始化,值为随机值;寄存器里的值未初始化为随机值。
- | static | auto(默认) | register |
---|---|---|---|
局部变量 | 全局数据区 | 栈空间 | 寄存器(可能) |
全局变量 | 全局数据区 | ---------------- | ------------------ |
21)C语言中,如果函数不写返回值类型,则默认返回类型是int;对于一个有返回值的函数,如果不写返回值,会返回一个随机值。
22)当参数只是一个字符串,且没提供长度的时候。可以使用while循环来遍历字符串
,判别依据是字符串的最后一个字符是0元素'\0'
。
23)在写递归函数的时候,首先要先把函数的递归模型画出来
,然后去编写递归函数。编写时候要注意:n-1
和边界
。在理解递归函数的时候,不要想着去执行递归的细节
,而是从递归模型来理解。
递归模型的一般表示法:
1、函数的概念
- 函数是具有特定
功能
的程序组件(可以看做黑盒:不需要知道里面怎么实现的,只知道怎么用) - 函数有明确的使用方式(固定输入对应固定输出)
- 函数在程序中可以重复使用(程序中的工具)
1.1 函数的类型
C语言中的函数主要有2种类型:
Function
:数据处理(数据 --> 数据),通过某种规则由输入的数据得到输出数据Procedure
:过程定义(数据 --> 功能),(根据数据)执行一系列动作,进而完成某种功能
1.2 函数的组成部分
- 函数名:函数的唯一标识
- 函数参数:数据输入(数据 --> 数据, 数据 --> 动作)
函数返回类型:
- 数据输出(数据 --> 数据)
- 无返回值(数据 --> 动作)
形如:
返回类型 函数名(参数1,参数2)
{
程序语句1;
...
程序语句1;
}
2、函数的调用
- 通过
函数名
调用已经定义好的函数 - 函数调用时需要依次指定函数参数的具体值
函数调用的结果(返回值)可保存在同类型的变量中
int r1 = func_name(5); int r2 = func_name(10);
2.1 深入理解main()
一般情况下,C语言程序都是从main()开始执行的,那么main()是什么呢?
- main()是应用程序与操作系统的一个“约定”,程序的
入口函数
- 当操作系统运行应用程序时,
首先调用的就是main()
函数 - 应用程序必须运行于操作系统,接受操作系统的管理(如windows任务资源管理器,可以随意结束某个应用进程)
在windows上运行程序,都是双击App运行的,为什么说应用程序是被操作系统运行的呢?
应用程序执行的过程:(后续补上时序图)
- 用户双击.exe,或者在命令行中直接运行.exe
- OS把应用程序加载到内存中:Load()
- OS找到应用程序的main()函数
- 执行main()函数
- 关闭应用程序时,main()函数执行结束,
返回一个值给操作系统
(在windows中,可以在命令行,使用echo %errorlevel%
命令查看刚刚命令执行的exe的返回值)
2.2 函数的调用过程
C程序的本质是由一系列不同功能的函数构成;函数之间通过相互调用组合“小功能”构成“大功能”;整个C程序的功能由函数的组合调用完成。
函数的调用过程:
- 暂停主调函数
- 跳转到被调函数,执行被调“函数体”
- 被调函数返回,恢复执行主调函数
之前的文章中一直在说“工具包”
这个概念,实际上,工具包就是一个函数集
,包含了一系列定义好的函数。#inlude语句用于声明,要使用工具包中的函数
。
3、函数的定义
3.1 函数中的void
函数定义与函数调用:
- 函数在被调用前必须完整定义(实现函数体)
函数可以先被声明,然后再被定义
- 声明时,必须给出
函数三要素
(函数名,参数列表,返回类型) - 定义时,必须完整给出函数体定义
- 声明时,必须给出
在函数调用之前如果函数没有声明,那么会报not delared错误;如果声明了,但是没定义,就会报undefined reference错误;(声明
就是告诉编译器,我这个符号是有的,尽管用,去其他地方找定义就好了)
void的深入理解:
- C语言中存在
空类型(void)
,这种类型表示“空” - void不能用于定义具体变量(没有数据属于空类型)
- void常用于函数定义,表示
没有返回值或无参数
3.2 函数的返回值
- return语句结束当前函数执行,返回主调函数,后续代码不再执行
对于
无返回值
函数(procedure
)- return可以直接返回,无需跟上返回值;即
return;
- 当函数体里没有return时,最后一条语句执行后自动返回
- return可以直接返回,无需跟上返回值;即
对于
有返回值
的函数(function
)- return必须跟上一个合法返回值,
所有的分支都得显示返回值
- return语句必须出现在函数体中,并且必须被执行
- return必须跟上一个合法返回值,
4、函数的参数
- 函数参数在函数定义时并没有确定的值(
形参
) - 函数参数的具体值再函数调用时指定(
实参
) - 函数参数的
本质是变量
4.1 普通参数
函数调用是指定的实参用于对形参进行初始化
,初始化之后形参在函数内部等同于普通变量
。
int add(int a, int b)
{
return a+b;
}
int main()
{
int c = add(1, 2);
printf("c = %d\n", c);
return 0;
}
4.2 数组参数
- 定义函数时可以使用
数组形参
(如int f(int a[5]);) - 数组形参需要使用
同类型数组
作为实参 - C语言中,数组作为函数参数传递时大小信息丢失(
数组参数退化为指针
) - 在数组内部修改数组形参,将影响实参
一般的,当使用数组作为参数时,函数的参数列表需要多一个参数来表示数组的大小。
4.3 数组排序
排序中的两个关键操作:
比较
:任意两个数据元素通过比较操作确定先后次序交换
:数据元素之间需要通过交换才能得到预期结果
解决方案:
编写函数int Min(int a[], int b, int e)
选择最小元素
- 功能定义:在数组a的[b,e]范围内寻找最小元素
- 返回值:最小元素在数组中的下标
循环遍历数组,将每次找到的最小元素
交换
for(i=0; i<n; i++) { j = Min(a, i, n-1); if(i != j) Swap a[i] and a[j]; }
5、变量的作用域和生命期
如果两个函数中有同同名变量,会发生命名冲突的问题么?
C语言中变量的分类:
局部变量
:函数内部定义的变量(隶属于当前函数),只能在当前函数中访问全局变量
:不特定隶属于某个函数,在任意函数中都可以访问
同名变量的问题:
- 不同函数中的局部变量可以同名(不会发生冲突)
- 全局变量不能同名(会产生
命名冲突
) - 当局部变量和全局变量同名时,
优先使用局部变量
5.1 作用域
变量的作用域:
变量的作用域指的是变量定义后的可访问范围
不同变量的作用域可以有重叠
- 不同变量在重叠作用域内可分别访问
- 在重叠作用域内,
只可访问最近定义的同名变量
局部变量的作用域:
代码块
:从{
开始到}
结束的一段代码- 经典C语言中,变量只能定义在代码块的开始处,即:{ 之后,执行语句之前
变量的作用域从定义开始到当前代码块结束
- 当变量的作用域结束后,
变量不可用
(无法直接访问)
全局变量的作用域:
全局作用域
:可在程序的各个角落访问并使用文件作用域
:只能在当前代码文件中访问并使用- 全局变量的作用域可能被局部变量覆盖(同名局部变量)
- 工程开发中,全局变量通常以g_作为前缀名
5.2 生命期
问题:为什么离开作用域之后变量无法使用?可以创建变量,那么是否也可以销毁变量?
不同变量的物理存储区域:
- 在现代计算机系统中,物理内存被分为不同区域
区域不同,用途不同,不同种类的变量位于不同区域
- 全局数据区:存放全局变量,静态变量
- 栈空间:存放函数参数,局部变量
- 堆空间:用于动态创建变量
生命期
:变量从创建到销毁的时间
,即合法可用的时间
不同变量的生命期
- 全局数据区中的变量:程序开始运行时创建,程序结束时销毁,整个程序运行期间合法可用
- 栈空间中的变量:进入作用域时创建,离开作用域时销毁(自动销毁),局部变量在函数调用返回后销毁
5.3 作用域和生命期?
问题:感觉上,变量的作用域直接决定了生命期;那么作用域和生命期之间有关系么?
作用域和生命期无本质联系:
- 作用域规则是
语法层面
对变量是否可访问的规定(空间上
) 生命期是
二进制层面
上变量存在于内存中的时间(时间上
)可能的情况
- 作用域外无法访问的变量,可能在其生命期中(静态局部变量)
- 作用域内可访问的变量,可能已经被销毁(堆变量)
- 生命期中的变量,可能无法访问(文件作用域全局变量)
静态变量:
- static是C语言中的关键字
- static修饰的局部变量创建于
全局数据区
(拥有程序生命期) - static修饰的全局变量只有
文件作用域
(文件之外无法访问) - static局部变量只会初始化一次,作用域与普通变量无异
变量的生命期由变量存储位置决定
:
- static变量存储于全局数据区,默认初始化为
0
- auto将变量存储于栈空间,默认初始化为
随机值
- register将变量存储于寄存器,默认初始化为
随机值
6、递归函数简介
问题:函数能不能自己调用自己?
函数自己调用自己
,就是递归。
递归是一种数学上分而自治
的思想
- 将原问题分解为规模较小的问题进行处理
- 问题的分解是有限的(递归不能无限进行)
例如:数列求和,
令:Sn为a1 + a2 + ... +an
则:Sn = Sn-1
+ an,且S1
= a1
。将问题向下往n-1
去分解,且有边界
,如n=1时。
递归模型的一般表示法:
本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。
如有错漏之处,恳请指正。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。