1

Summary

1)C语言中的函数主要有2种类型:

  • Function:数据处理(数据 --> 数据),通过某种规则由输入的数据得到输出数据
  • Procedure:过程定义(数据 --> 功能),(根据数据)执行一系列动作,进而完成某种功能

2)应用程序必须依靠操作系统才能运行,应用程序接受操作系统的管理。当操作系统运行程序时,首先调用的就是main()函数。)(main()函数一般是第一个被调用的函数,也有其他方法可以让其他函数先于main执行)

3)应用程序执行的过程:(后续补上时序图)

  1. 用户双击.exe,或者在命令行中直接运行.exe
  2. OS把应用程序加载到内存中:Load()
  3. OS找到应用程序的main()函数
  4. 执行main()函数
  5. 关闭应用程序时,main()函数执行结束返回一个值给操作系统(在windows中,可以在命令行,使用echo %errorlevel%命令查看刚刚命令执行的exe的返回值)

4) 函数的调用过程:

  1. 暂停主调函数
  2. 跳转到被调函数,执行被调“函数体”
  3. 被调函数返回,恢复执行主调函数

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段);栈区的变量如果未初始化,值为随机值;寄存器里的值未初始化为随机值。

-staticauto(默认)register
局部变量全局数据区栈空间寄存器(可能)
全局变量全局数据区----------------------------------

21)C语言中,如果函数不写返回值类型,则默认返回类型是int;对于一个有返回值的函数,如果不写返回值,会返回一个随机值。

22)当参数只是一个字符串,且没提供长度的时候。可以使用while循环来遍历字符串,判别依据是字符串的最后一个字符是0元素'\0'

23)在写递归函数的时候,首先要先把函数的递归模型画出来,然后去编写递归函数。编写时候要注意:n-1边界。在理解递归函数的时候,不要想着去执行递归的细节,而是从递归模型来理解。
递归模型的一般表示法:
image.png

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运行的,为什么说应用程序是被操作系统运行的呢?
应用程序执行的过程:(后续补上时序图)

  1. 用户双击.exe,或者在命令行中直接运行.exe
  2. OS把应用程序加载到内存中:Load()
  3. OS找到应用程序的main()函数
  4. 执行main()函数
  5. 关闭应用程序时,main()函数执行结束,返回一个值给操作系统(在windows中,可以在命令行,使用echo %errorlevel%命令查看刚刚命令执行的exe的返回值)

2.2 函数的调用过程

C程序的本质是由一系列不同功能的函数构成;函数之间通过相互调用组合“小功能”构成“大功能”;整个C程序的功能由函数的组合调用完成。

函数的调用过程:

  1. 暂停主调函数
  2. 跳转到被调函数,执行被调“函数体”
  3. 被调函数返回,恢复执行主调函数

之前的文章中一直在说“工具包”这个概念,实际上,工具包就是一个函数集,包含了一系列定义好的函数。#inlude语句用于声明,要使用工具包中的函数

3、函数的定义

3.1 函数中的void

函数定义与函数调用:

  • 函数在被调用前必须完整定义(实现函数体)
  • 函数可以先被声明,然后再被定义

    • 声明时,必须给出函数三要素(函数名,参数列表,返回类型)
    • 定义时,必须完整给出函数体定义

在函数调用之前如果函数没有声明,那么会报not delared错误;如果声明了,但是没定义,就会报undefined reference错误;(声明就是告诉编译器,我这个符号是有的,尽管用,去其他地方找定义就好了)

void的深入理解:

  • C语言中存在空类型(void),这种类型表示“空”
  • void不能用于定义具体变量(没有数据属于空类型)
  • void常用于函数定义,表示没有返回值或无参数

3.2 函数的返回值

  • return语句结束当前函数执行,返回主调函数,后续代码不再执行
  • 对于无返回值函数(procedure

    • return可以直接返回,无需跟上返回值;即 return;
    • 当函数体里没有return时,最后一条语句执行后自动返回
  • 对于有返回值的函数(function

    • 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时。

递归模型的一般表示法:
image.png

本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。
如有错漏之处,恳请指正。


bryson
169 声望12 粉丝