21天学通C语言-学习笔记(8)

函数

10.1 函数概述

  • 模块化编程思想:

将一些功能相互独立的模块分开,最后合并到主程序的编程方法。C语言中,把功能独立且完整的的模块封装在函数中,由函数提供对外的接口,再在外部函数调用。

具体执行顺序如下:当 main 函数运行到函数 m 时,就会查找这个函数定义的函数体,依次执行这些代码;然后继续回到 main 函数,继续执行下一语句。这就是模块化编程的好处,程序规模越大,优势越明显。

  • 下面来说说函数是如何定义的:

函数返回值类型 函数名(参数类型1 参数名1,参数类型2 参数名2,…){
变量定义;
函数操作;
return 语句;
}

解读:第一行为函数声明,第二行为函数体,函数定义由这两部分组成,缺一不可,但函数声明可以单独作为语句使用。

  • 定义之后就要调用函数:

函数名(参数表达式1,参数表达式2,…)

  • 下面来说说神秘的实参:函数调用语句中的参数表达式,由于具有实际值,也被称为“实际参数”,简称为“实参”。

  • 知道实参了,那么形参是什么:函数声明和函数定义中的参数,在函数被调用前,不占用内存中的存储空间,有形无实,所以被称为“形式参数”,简称为“形参”。

  • 再来说一说实参值传递给函数定义中的各个形参的过程叫做参数传递,这时,形参会分配到内存空间,执行 return
    语句后,将函数返回值带入到函数调用表达式所在语句。

  • 具体来说一说函数声明:完整的函数声明定义了一个函数接口的模块,标准形式如下:

  • 函数值类型 函数名 (参数类型1 形参1,参数类型2 形参2,…);

    注意事项:函数被调用之前必须有函数声明,如果函数定义发生在函数调用之后,那么,必须在函数调用前使用单独的函数声明语句。这与使用变量钱必须有变量声明是一个道理。另外,一个函数变量可以被声明多次,但是函数定义只能有一个。

  • 接下来我们说一说函数声明中的函数返回值类型,也叫函数值类型:函数返回值类型可以是整型、浮点型、字符型,也可以是自定义的类型(typedef
    的结果)。不能为数组型或者函数型,如必须使用,则需要用到指针,我们下节说一说神秘的指针。

    以上这些函数值类型的函数在被调用以后,都会返回一个函数值作为其函数调用表达式的值,该函数值可以作为函数调用表达式中其余操作符的操作数。罢特,如果函数值类型是
    void(空的) 型,没有返回值,更禁止返回值作为操作数的使用。

  • 然后就到了形参列表的出场时间了:当形参列表作为函数定义一部分的函数声明时,形参列表必须为每个参数声明类型和参数名。但在函数定义外的函数声明中,参数列表中可以只列出参数类型。

  • 下面是函数体。C语言中的变量定义必须放在函数体的最前面,允许变量定义之间有空行,但不能包括函数操作。

  • 重点来了:main 函数。main 函数的返回值类型必须为 int ,而形参列表可以为空(void),也可以带两个形参。而且,只接受返回值类型为 int 型的 main 函数,强调是 int 型。

这就是函数的相关基础知识了,最特殊的函数就是 main 函数了,main 函数的声明必须完整,要包括 int 型的函数值类型和 return 语句。

10.2 函数与文件

  • 什么是文件:文件是与函数平级的一种模块单元,是用来处理规模较大、逻辑繁杂的程序的,包括源文件和头文件
    每一个源文件都会有一个头文件作为源文件之间的接口,这相当于函数模块里的模块接口——函数声明,即可能会被外部使用的部分。一般包括:类型声明、函数声明、常量定义。
  • 嵌套调用与递归调用
    嵌套调用:在调用A函数的过程中,又调用了B函数 ,这种调用称为嵌套调用。
    递归调用:一种是在函数内部调用函数本身,叫做直接递归调用,如果没有结束的命令(收敛性),便会陷入死循环;另一种是间接递归调用,是指函数 A 调用函数 B ,B 函数调用 C 函数,C 函数调用 D 函数…直到某个函数又重新调用了函数 A ,构成一个循环调用的结构。解决问题的典型是求阶乘、求 Fibonacci 数列

上图为Fibonacci 数列代码在这里插入图片描述

// 用递归函数来计算阶乘
#include <stdio.h>

int main(void)
{

      return 0;
}

int jiecheng (int n)
{
      if (n < 1)
      {
	      print("n必须大于等于1.\n");
	      return -1;
      }

	if (n == 1)
	{
		return 1;
	}
	else
	{
		return (n * jiecheng (n-1));
	}
}

函数的递归调用原理实际上是在栈内存上执行的,所以,栈内存的大小是限制递归深度的重要因素,否则就是栈溢出。
友情提示:尽量少使用递归,尽可能考虑使用循环结构来代替递归函数。

  • 外部函数
    #include 可以用来实现外部文件的函数调用,除此之外,还有一种方法调用外部函数:
    什么叫外部函数:关键字 extern 声明的类型,是文件间函数的调用。标准形式:extern 返回值类型 函数名(形参列表);
    extern作用有两个: 一是在没有定义某函数的源文件里使用 extern 声明该函数,便可以在这个文件内使用该函数;二是在函数定义的函数声明中使用关键字 extern ,那么,该函数就允许被其他文件的函数使用。其实,在C语言中,所有函数默认声明为 extern 类别,即外部函数。

  • 内部函数:关键字 static 声明的类型,顾名思义,只用于本文件,对外不可见 。标准形式:static 返回值类型 函数名(形参列表);

10.3 函数中的变量

  • 生存期和作用域(第一次听说):

    生存期是指程序运行时,变量占用内存的时间。具体来说,当程序运行到变量的定义语句时,编译器为其分配内存,这是生存期的开始;当变量占用的内存被释放时,则标志着生存期的结束。

    作用域是指变量可以被使用的有效代码区域。在下层程序块定义的内部变量在上层程序块和外部程序块中是不可见的。

  • 根据变量的作用域可以将变量分为局部变量和全局变量,下面分别说一下局部变量和全局变量。

    局部变量是指作用域无法涵盖整个代码区的变量,优点是仅在需要时编译器才为之分配内存。每次调用时的局部变量都是新生成的、并拥有不同内存空间的变量,局部变量的作用域不能超过所属的函数体。

    全局变量也称作外部变量,即在函数外部定义的变量。全局变量的作用域是其下层程序块。常常在全局变量前加g_,g 为 global
    的意思。每个函数对全局变量值的改变会保存在内存中。

  • 如何合理使用全局变量:
    首先初始化全局变量:如果全局变量没有为其显式初始化,编译器则会自动初始化,将其内存空间清除归零。其次需要注意1、过多使用全局变量导致资源浪费
    2、不宜对全局变量做过多操作 3、不利于模块化设计,牺牲了函数的独立性和信息的封装性 4、破坏信息封装性

C语言有两个属性:数据类型和存储类别

这里详细介绍存储类别:4个关键字作为存储类别的修饰词 auto、static、register 和 extern。变量的存储类别决定了变量在内存中的存储区域,存储区分为4种:栈、堆、静止存储区和常量存储区。

栈:栈是由编译器管理的动态存储区域,用于存储临时变量。(函数形参、局部变量、其他临时变量)
堆:堆是由程序管理的动态存储区域,用于分配由程序 malloc 函数申请的内存空间,需要由程序自行释放,以及显示初始化。
静态存储区:用于存储全局变量,在开始时就已经分配完毕,直到程序结束由编译器自动释放。(全局变量)
常量存储区:用于存储程序中的常量。

auto 变量:声明变量的生存期为自动型,内存从栈上分配,为临时变量。声明变量的形式:auto 数据类型 变量名; 或者是 数据类型 auto 变量名; 所有局部变量的声明中如果不含存储类别,都默认为 auto 型变量。注意不能在函数外部声明 auto 变量。

static 变量:静止型,内存从静止存储区分配,生存期为整个程序的执行过程。形式: static 数据类型名 变量名; static 局部变量在函数结束时能保留住内存而不释放;static 全局变量将变量限制在本文件中使用。static 变量只能在本程序层及其下层的程序块内使用。

register 变量:正常情况下,当程序访问变量时,计算机要先从内存中将变量值提取到寄存器中;运算结束后,如果该变量值发生改变,则还需要把寄存器中的值重新送回到内存存放。而register 这种存储类别的变量的值会被要求直接存储在寄存器中。注:寄存器的存取速度远快于内存的存取速度。所以此变量适用于被频繁使用的变量。形式:register 数据类型名 变量名;
注意:register 变量只能修饰函数内的变量,包括局部变量和形式参数;一个变量只能声明为一种存储类别;不能对 register 变量进行取地址操作。

extern 变量:声明全局变量。用extern 声明此变量之后,程序会在该程序层外寻找这个变量的定义。

以上就是函数与函数变量的相关知识了。

字符串处理函数

C 语言为字符串专门定义了几组输入/输出函数,其中包括 putchar 和 getchar 函数,它们提供了字符的输入和输出操作;puts 函数和 gets 函数,它们提供了字符串的输入和输出操作。

复制和拼接函数也是应用频繁的操作,C语言标准库提供了 strcpy 函数和 strncpy 函数来实现字符串复制的功能,并提供了 strcat 函数和 strncat 函数来实现字符串拼接的功能。(没听说过)

比较和转换函数:strncmp 和 strcmp(字符串首地址比较函数)、strupr (小转大)和 strlwr (大转小)函数(字符串大小写转换函数)。

这里又补充了一点“字符串处理函数”的知识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值