【跟着陈七一起学C语言】今天总结:C语言的函数相关知识

news2025/6/16 16:22:18

友情链接:专栏地址

知识总结顺序参考C Primer Plus(第六版)和谭浩强老师的C程序设计(第五版)等,内容以书中为标准,同时参考其它各类书籍以及优质文章,以至减少知识点上的错误,同时方便本人的基础复习,也希望能帮助到大家
 
最好的好人,都是犯过错误的过来人;一个人往往因为有一点小小的缺点,将来会变得更好。如有错漏之处,敬请指正,有更好的方法,也希望不吝提出。最好的生活方式就是和努力的大家,一起奔跑在路上


文章目录

  • 【思维导图】
  • 🚀一、为什么要使用函数
  • 🚀二、函数的定义
      • ⛳(一)定义无参函数
      • ⛳(二)定义有参函数
      • ⛳(三)定义空函数
  • 🚀三、函数的调用和声明
      • ⛳(一)函数的调用
      • ⛳(二)函数的声明
  • 🚀四、函数参数
      • ⛳(一)形式参数和实际参数
        • 🎈1.实参和形参间的数据传递
        • 🎈2.函数调用的过程
      • ⛳(二)使用数组作为函数参数
        • 🎈1.数组元素作实参
        • 🎈2.一维数组名作函数参数
        • 🎈3.多维数组名作函数参数
  • 🚀六、函数的栈空间
  • 🚀七、函数的嵌套和内联、递归函数
    • ⛳(一)函数的嵌套
    • ⛳(二)内联函数
    • ⛳(三)递归函数
  • 🚀八、变量
    • ⛳(一)局部变量和全局变量
      • 🎈1.局部变量
      • 🎈2.全局变量
    • ⛳(二)变量的存储方式和生存期
      • 🎈1.动态存储方式和静态存储方式
      • 🎈2.局部变量的存储类别
        • (1)自动变量(auto变量)
        • (2)静态局部变量(static局部变量)
        • (3)寄存器变量(register变量)
      • 🎈3.全局变量的存储类别
        • (1)extern声明
          • ①在一个文件内拓展外部变量的作用域
          • ②将外部变量的作用域扩展到其他文件
        • (2)static静态外部变量
  • 🚀九、内部函数和外部函数
    • ⛳(一)内部函数
    • ⛳(二)外部函数


【思维导图】

在这里插入图片描述

我的总结方式一般是先搞一段程序过来,然后再逐行的学习相关的知识点

🚀一、为什么要使用函数

  1. 首先,使用函数可以省去编写重复代码的苦差。如果程序要多次完成某项任务,那么只需编写一个合适的函数,就可以在需要时使用这个函数,或者在不同的程序中使用该函数,就像许多程序中使用 putchar()一样。
  2. 其次,即使程序只完成某项任务一次,也值得使用函数。因为函数让程序更加模块化,从而提高了程序代码的可读性,更方便后期修 改、完善。
  3. 避免“重复制造轮子”,提高开发效率
  4. 便于维护

模块化程序设计:使用函数封装代码的思路
 
函数就是功能。每一个函数用来实现一个特定的功能。函数的名字应反映其代表的功能。

 

🚀二、函数的定义

C语言要求,在程序中用到的所有函数,必须“先定义,后使用”。例如想用max函数去求两个数中的大者,必须事先按规范对它进行定义,指定它的名字﹑函数返回值类型、函数实现的功能以及参数的个数与类型,将这些信息通知编译系统。这样,在程序执行max时,编译系统就会按照定义时所指定的功能执行。如果事先不定义,编译系统怎么能知道max是什么、要实现什么功能呢!

定义函数应包括以下几个内容:

  1. 指定函数的名字,以便以后按名调用。
  2. 指定函数的类型,即函数返回值的类型。
  3. 指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项。
  4. 指定函数应当完成什么操作,也就是函数是做什么的,即函数的功能。这是最重要的,是在函数体中解决的

对于C编译系统提供的库函数,是由编译系统事先定义好的,库文件中包括了对各函数的定义。程序设计者不必自己定义,只须用#include 指令把有关的头文件包含到本文件模块中即可。在有关的头文件中包括了对函数的声明。例如,在程序中若用到数学函数(如sqrt,fabs,sin,cos等),就必须在本文件模块的开头写上:
 
include <math.h>

函数的设计方法:

  1. 先确定函数的功能
  2. 确定函数的参数,是否需要参数,参数的个数,参数的类型
  3. 确定函数的返回值 是否需要返回值,返回值的类型
  4. 确定函数名,函数名, 一定要顾名思义.
  5. 函数名的命名方法, 和变量名相同
  6. 函数的实现

⛳(一)定义无参函数

类型名 函数名()
{
	函数体
}

//或

void test(void)
{
	函数体
}

第一个void表示函数返回值为空

函数名后面括号内的void表示“空”,即函数没有参数。

⛳(二)定义有参函数

类型名 函数名(形式参数列表)
{
	函数体
}

//例如:
int max(int x,int y)
{
    int z;
    z = x>y? x:y;
    return z;
}

形参列表中的x和y就是max函数的参数,同时也需要参数指定类型,这里的x和y都是int型

return(z)的作用(可以带括号也可以不带)是指定将z的值作为函数值(称函数返回值)带回到主调函数。

⛳(三)定义空函数

类型名 函数名()
{
}

//例如:
void example()
{    
}

为什么要特地强调定义一个空函数呢:

在程序设计中往往根据需要确定若干个模块,分别由一些函数来实现。而在第1阶段只设计最基本的模块,其他一些次要功能或锦上添花的功能则在以后需要时陆续补上。在编写程序的开始阶段,可以在将来准备扩充功能的地方写上一个空函数(函数名取将来采用的实际函数名(如用merge( ) , matproduct() , concatenate()和 shell()等,分别代表合并、矩阵相乘、字符串连接和希尔法排序等),只是这些函数暂时还未编写好,先用空函数占一个位置,等以后扩充程序功能时用一个编好的函数代替它。这样做,程序的结构清楚,可读性好,以后扩充新功能方便,对程序结构影响不大。空函数在程序设计中常常是有用的。

 

🚀三、函数的调用和声明

⛳(一)函数的调用

定义函数的目的是为了调用此函数,以得到预期的结果。

函数调用的三种方式:

  1. 函数调用语句:把函数调用单独作为一个语句,如:printf();

  2. 函数表达式:函数调用出现在另一个表达式中,如:c = 2 * max(a,b);

  3. 函数参数:函数调用作为另一个函数调用时的参数,如:m = max(a,max(b,c));

⛳(二)函数的声明

在一个函数中调用另一个函数(即被调用函数)需要具备如下条件:

  1. 被调函数是已经定义的函数(库函数或用户自己定义的函数)
  2. 如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中来。例如,前几章中已经用过的指令:include <stdio.h>
  3. 如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明(declaration)。声明的作用是把函数名,函数参数的个数和参数类型等信息通知编译系统﹐以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。

在这里插入图片描述

函数的声明和函数定义中的第1行(函数首部)基本上是相同的,只差一个分号(函数声明比函数定义中的首行多一个分号)。因此写函数声明时,可以简单地照写已定义的函数的首行,再加一个分号,就成了函数的“声明”。函数的首行(即函数首部)称为函数原型(function prototype)。
 
拓展:
 
1.在函数声明中的形参名可以省略,而只写形参的类型

 

🚀四、函数参数

⛳(一)形式参数和实际参数

定义函数时函数名后面括号中的变量名称为“形式参数”(简称“形参”)或“虚拟参数”。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”(简称“实参”)。实际参数可以是常量,变量或表达式。

🎈1.实参和形参间的数据传递

调用函数时,形参被赋值为对应的实参, 实参本身不会受到函数的影响!

在这里插入图片描述

🎈2.函数调用的过程

  1. 在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数max的形参才被临时分配内存单元

  2. 将实参的值传递给对应形参。

  3. 在执行max函数期间,由于形参已经有值,就可以利用形参进行有关的运算

  4. 通过return语句将函数值带回到主调函数。返回值的类型与函数类型一致。

  5. 如果函数不需要返回值,则不需要return语句。这时函数的类型应定义为void类型。

  6. 调用结束,形参单元被释放。注意:实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数的实参的值。

    注意:实参向形参的数据传递是“值传递”,单向传递,只能由实参传给形参,而不能由形参传给实参。实参和形参在内存中占有不同的存储单元,实参无法得到形参的值。

⛳(二)使用数组作为函数参数

调用有参函数时,需要提供实参。例如sin(x), sqrt(2.0) , max(a,b)等。实参可以是常量、变量或表达式。数组元素的作用与变量相当,一般来说﹐凡是变量可以出现的地方﹐都可以用数组元素代替。

  • 因此,数组元素也可以用作函数实参,其用法与变量相同,向形参传递数组元素的值。
  • 此外,数组名也可以作实参和形参,传递的是数组第一个元素的地址。

🎈1.数组元素作实参

数组元素可以用作函数实参,但是不能用作形参。因为形参是在函数被调用时临时分配存储单元的,不可能为一个数组元素单独分配存储单元(数组是一个整体,在内存中占连续的一段存储单元)。在用数组元素作函数实参时,把实参的值传给形参,是“值传递”方式。数据传递的方向是从实参传到形参,单向传递。

🎈2.一维数组名作函数参数

除了可以用数组元素作为函数参数外,还可以用数组名作函数参数(包括实参和形参)。

注意:用数组元素作实参时,向形参变量传递的是数组元素的值,而用数组名作函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。

float average(float array[10]);						//函数声明

float average( float array[1o])						//定义average函数				
{
    int i;
    float aver,sum=array[o];
    for(i=1;i≤10;i十+)
    	sum=sum+array[i];							//累加学生成绩
    aver=sum/10;
    return(aver);
}
  1. 注意主调函数(这里是main函数)与被调用函数(average)的位置,如果主调函数在被调用函数定义前,要记得声明函数
  2. 实参数组与形参数组类型应一致(今都为float型),如不一致,结果将出错。
  3. 在定义average函数时,声明形参数组的大小为10,但在实际上,指定其大小是不起任何作用的,因为C语言编译系统并不检查形参数组大小,只是将实参数组的首元素的地址传给形参数组名。临时分配的形参数组空间,形参数组首元素(array[O])和实参数组首元素(score[o])具有同一地址,它们共占同一存储单元, score[n]和 array[n]指的是同一单元。score[n]和array[n]具有相同的值。
  4. 形参数组可以不指定大小,在定义数组时在数组名后面跟一个空的方括号

🎈3.多维数组名作函数参数

多维数组元素可以作函数参数,这点与前述的情况类似。

可以用多维数组名作为函数的实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明。例如:

int array[3][10];int array[][10];

二者都合法而且等价。但是不能把第⒉维以及其他高维的大小说明省略。

 

🚀六、函数的栈空间

当调用一个函数时,就会在栈空间,为这个函数,分配一块内存区域, 这块内存区域,专门给这个函数使用。 这块内存区域,就叫做“栈帧”。

在这里插入图片描述

 

🚀七、函数的嵌套和内联、递归函数

⛳(一)函数的嵌套

C语言的函数定义是互相平行.独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用一个函数的过程中﹐又调用另一个函数,

在这里插入图片描述

⛳(二)内联函数

内联函数的用法:

inline int add(int a, int b)
{ 
return a + b; 
}

函数的缺点: 每调用一次函数,就会为这个函数分配一个“栈”, 在计算机底层做很多准备工作(保护原来的执行环境,切换到新的执行环境) 有一定的“时间开销”,解决方案: 使用内联函数

当编译器在编译时, 如果遇到内联函数, 就会直接将整个函数体的代码插入”调用处”, 就相当于内联函数的函数体, 在调用处被重写了一次。 以避免函数调用的开销, 获得更快的时间

内联函数的缺点: 使调用内联函数的程序,变得“臃肿”,消耗主调函数的“栈”空间。

内联函数的使用场合:

  1. 内联函数中的代码应该只是很简单、执行很快的几条语句。
  2. 这个函数的使用频度非常高,比如在一个循环中被千万次地使用。

⛳(三)递归函数

在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。

  1. 设计递归函数的要点:把问题拆解成问题本身, 但是拆解后的问题的”规模”更小, 或者难度更低.
  2. 再定义递归函数时,一定要确定一个“结束条件”!!!
  3. 递归函数的缺点: 性能很低!!! 实际开发中, 极少使用

在这里插入图片描述

经典实例:斐波那契数列、汉诺塔问题

/*
斐波那契数列
1, 1, 2, 3, 5, 8, 13, 21, .... 计算第 n 个数是多少?

//分析:
f(n)
当 n >2 时,f(n) = f(n-1) + f(n-2)
当 n=1 或 n=2 时, f(n)就是 1
f(8) = f(7) + f(6)


//实现:
int fib(int n) {
    int s;
    
    if (n == 1|| n == 2) {
    	return 1;
    }
    
    s = fib(n-1) + fib(n-2);
    
    return s;
}
*/

 

🚀八、变量

在一个函数中定义的变量,在其他函数中能否被引用?在不同位置定义的变量,在什么范围内有效?这就是变量的作用域问题。每一个变量都有一个作用域问题,即它们在什么范围内有效。本节专门讨论这个重要问题。

⛳(一)局部变量和全局变量

🎈1.局部变量

定义变量可能有3种情况:

  1. 在函数的开头定义;
  2. 在函数内的复合语句内定义
  3. 在函数的外部定义。

在一个函数内部定义的变量只在本函数范围内有效,也就是说只有在本函数内才能引用它们,在此函数以外是不能使用这些变量的。在复合语句内定义的变量只在本复合语句范围内有效,只有在本复合语句内才能引用它们。在该复合语句以外是不能使用这些变量的,以上这些称为“局部变量”。

主函数中定义的变量(如 m, n)也只在主函数中有效﹐并不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
 
不同函数中可以使用同名的变量,它们代表不同的对象,互不干扰。
 
形式参数也是局部变量。
 
在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。

🎈2.全局变量

前已介绍,程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全局变量(也称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。

但是,建议不在必要时不要使用全局变量,原因如下:

  1. 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。

  2. 它使函数的通用性降低了,因为如果在函数中引用了全局变量,那么执行情况会受到有关的外部变量的影响,如果将一个函数移到另一个文件中,还要考虑把有关的外部变量及其值一起移过去。但是若该外部变量与其他文件的变量同名时,就会出现问题。这就降低了程序的可靠性和通用性。

    在程序设计中,在划分模块时要求模块的“内聚性”强、与其他模块的“耦合性”弱。即模块的功能要单一(不要把许多互不相干的功能放到一个模块中),与其他模块的相互影响要尽量少,而用全局变量是不符合这个原则的。一般要求把C程序中的函数做成一个相对的封闭体,除了可以通过“实参—形参”的渠道与外界发生联系外,没有其他渠道。这样的程序移植性好,可读性强。

  3. 使用全局变量过多﹐会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。由于在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。

  4. 当全局变量与局部变量同名时,在相同的作用域部分,局部变量的作用域会覆盖全局变量的作用域

⛳(二)变量的存储方式和生存期

🎈1.动态存储方式和静态存储方式

从变量的作用域(即从空间)的角度来观察,变量可以分为全局变量和局部变量。

还可以从另一个角度﹐即从变量值存在的时间(即生存期)来观察。有的变量在程序运行的整个过程都是存在的,而有的变量则是在调用其所在的函数时才临时分配存储单元,而在函数调用结束后该存储单元就马上释放了,变量不存在了。也就是说,变量的存储有两种不同的方式:静态存储方式和动态存储方式。

静态存储方式:指在程序运行期间由系统分配固定的存储空间的方式,

动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。

内存中供用户使用的存储空间:

在这里插入图片描述

动态存储区:

  • 函数形式参数。在调用函数时给形参分配存储空间。
  • 函数中定义的没有用关键字static声明的变量,即自动变量(详见后面的介绍)。
  • 函数调用时的现场保护和返回地址等

对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一函数,而在此函数中定义了局部变量,在两次调用时分配给这些局部变量的存储空间的地址可能是不相同的。

数据分别存放在静态存储区和动态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。

🎈2.局部变量的存储类别

在C语言中,每一个变量和函数都有两个属性:数据类型和数据的存储类别。对数据类型,读者已经熟知(如整型、浮点型等)。存储类别指的是数据在内存中存储的方式(如静态存储和动态存储)。
 
在定义和声明变量和函数时,一般应同时指定其数据类型和存储类别,也可以采用默认方式指定(即如果用户不指定﹐系统会隐含地指定为某一种存储类别)。
 
C的存储类别包括4种:自动的( auto)、静态的( statis)、寄存器的(register)、外部的( extern)。根据变量的存储类别,可以知道变量的作用域和生存期。下面分别作介绍。

(1)自动变量(auto变量)

函数中的局部变量,如果不专门声明为static(静态)存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的局部变量(包括在复合语句中定义的局部变量),都属于此类。在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明

实际上,关键字auto可以省略,不写auto则隐含指定为“自动存储类别”,它属于动态存储方式。程序中大多数变量属于自动变量。前面几章中介绍的例子,在函数中定义的变量都没有声明为auto,其实都隐含指定为自动变量。

(2)静态局部变量(static局部变量)

有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次再调用该函数时,该变量已有值(就是上一次函数调用结束时的值)。这时就应该指定该局部变量为“静态局部变量”,用关键字static进行声明。

  1. 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。
  2. 对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。
  3. 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符’o’(对字符变量)。而对自动变量来说,它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,
  4. 虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。因为它是局部变量,只能被本函数引用,而不能被其他函数引用。

(3)寄存器变量(register变量)

一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数﹐再从运算器将数据送到内存存放,
 
在这里插入图片描述

如果有一些变量使用频繁(例如,在一个函数中执行10 000次循环,每次循环中都要引用某局部变量),则为存取变量的值要花费不少时间。为提高执行效率,允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄―图7.18存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字register作声明。

注意:3种局部变量的存储位置是不同的:自动变量存储在动态存储区﹔静态局部变量存储在静态存储区﹔寄存器存储在CPU中的寄存器中。

拓展:
 
由于现在的计算机的速度愈来愈快,性能愈来愈高,优化的编译系统能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。因此,现在实际上用register声明变量的必要性不大。

🎈3.全局变量的存储类别

全局变量都是存放在静态存储区中的。因此它们的生存期是固定的,存在于程序的整个运行过程。但是,对全局变量来说,还有一个问题尚待解决,就是它的作用域究竞从什么位置起,到什么位置止。作用域是包括整个文件范围还是文件中的一部分范围?是在一个文件中有效还是在程序的所有文件中都有效?这就需要指定不同的存储类别。

一般来说,外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。但有时程序设计人员希望能扩展外部变量的作用域。有以下几种情况。

(1)extern声明

①在一个文件内拓展外部变量的作用域

如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。在定义点之前的函数不能引用该外部变量。如果由于某种考虑,在定义点之前的函数需要引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

注意:提倡将外部变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern声明。
 
用extern声明外部变量时,类型名可以写也可以省写。

②将外部变量的作用域扩展到其他文件

一个C程序可以由一个或多个源程序文件组成。如果程序只由一个源文件组成,使用外部变量的方法前面已经介绍。如果程序由多个源程序文件组成,那么在一个文件中想引用另一个文件中已定义的外部变量,有什么办法呢?

如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,否则在进行程序的连接时会出现“重复定义”的错误。正确的做法是:在任一个文件中定义外部变量Num,而在另一文件中用extern对Num作“外部变量声明”,即“extern Num;”。在编译和连接时,系统会由此知道Num有“外部链接”,可以从别处找到已定义的外部变量Num,并将在另一文件中定义的外部变量Num的作用域扩展到本文件,在本文件中可以合法地引用外部变量Num。

有的读者可能会问:extern既可以用来扩展外部变量在本文件中的作用域,又可以使外部变量的作用域从一个文件扩展到程序中的其他文件,那么系统怎么区别处理呢?实际上,在编译时遇到extern时,先在本文件中找外部变量的定义﹐如果找到,就在本文件中扩展作用域;如果找不到,就在连接时从其他文件中找外部变量的定义。如果从其他文件中找到了,就将作用域扩展到本文件;如果再找不到,就按出错处理。

(2)static静态外部变量

有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。

这种加上static声明、只能用于本文件的外部变量称为静态外部变量。

说明:不要误认为对外部变量加static声明后才采取静态存储方式(存放在静态存储区中),而不加 static的是采取动态存储(存放在动态存储区)。声明局部变量的存储类型和声明全局变量的存储类型的含义是不同的。对于局部变量来说,声明存储类型的作用是指定变量存储的区域(静态存储区或动态存储区)以及由此产生的生存期的问题,而对于全局变量来说,由于都是在编译时分配内存的,都存放在静态存储区﹐声明存储类型的作用是变量作用域的扩展问题。

用static声明一个变量的作用是:

  1. 对局部变量用static声明,把它分配在静态存储区﹐该变量在整个程序执行期间不释放﹐其所分配的空间始终存在。
  2. 对全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。

注意:用auto,register和 static声明变量时,是在定义变量的基础上加上这些关键字,而不能单独使用。下面的用法不对:

int a;
//先定义整型变量a
static a;
//企图再将变量a声明为静态变量

编译时会被认为“重新定义”。

在这里插入图片描述
 

🚀九、内部函数和外部函数

前面提到有全局变量和局部变量,在函数中对应也有内部函数和外部函数的。有的函数可以被本文件中的其他函数调用,也可以被其他文件中的函数调用,而有的函数只能被本文件中的其他函数调用.不能被其他文件中的函数调用.

函数本质上是全局的,因为定义一个函数的目的就是要被另外的函数调用。如果不加声明的话,一个文件中的函数既可以被本文件中其他函数调用,也可以被其他文件中的函数调用。但是,也可以指定某些函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。

⛳(一)内部函数

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static,即:

static 类型名 函数名(形参列表)

内部函数又称静态函数,因为它是用static声明的。使用内部函数,可以使函数的作用域只局限于所在文件。这样,在不同的文件中即使有同名的内部函数,也互不干扰,不必担心所用函数是否会与其他文件模块中的函数同名。

⛳(二)外部函数

如果在定义函数时,在函数首部的最左端加关键字extern,则此函数是外部函数,可供其他文件调用。

extern 类型名 函数名(形参列表)

C语言规定,如果在定义函数时省略extern,则默认为外部函数。前面所用的函数都是外部函数。


行文至此,落笔为终。文末搁笔,思绪驳杂。只道谢不道别。早晚复相逢,且祝诸君平安喜乐,万事顺意。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

太阳能电池板AI视觉检测:不良品全程阻断,高效助力光伏扩产

2022年&#xff0c;面对复杂严峻的国内外形势&#xff0c;我国光伏行业依然实现高速增长&#xff0c;多晶硅、硅片、电池片、组件产量稳居全球首位。2023年以来&#xff0c;扩产项目已多点开花。光伏装机量天花板将不断提升&#xff0c;分布式电站占比也将逐年上升。中国光伏行…

4月软件测试面试太难,吃透这份软件测试面试笔记后,成功跳槽涨薪30K

4 月开始&#xff0c;生活工作渐渐步入正轨&#xff0c;但金三银四却没有往年顺利。昨天跟一位高级架构师的前辈聊天时&#xff0c;聊到今年的面试。有两个感受&#xff0c;一个是今年面邀的次数比往年要低不少&#xff0c;再一个就是很多面试者准备明显不足。不少候选人能力其…

python学籍管理系统

1&#xff0c;创建登陆的首页面&#xff0c;且封装起来。LoginPage.py import tkinter as tk#导入tk模块 from tkinter import messagebox#导入消息提示模块 from tkinter import messagebox from db import db #导入数据库db class LoginPage:#把整个登陆页面创建一个class类…

搭建自己的饥荒Don‘t Starve服务器-饥荒Don‘t Starve开服教程

前言 饥荒这个游戏&#xff0c;虽然首发于2016年&#xff0c;但是贵在好玩呀。和Minecraft一样&#xff0c;可玩性很高&#xff0c;并且有很多mods&#xff0c;最近和小伙伴玩的过程中&#xff0c;就想着搭建一个服务器&#xff0c;方便在主机玩家不在线时候&#xff0c;也可以…

Linux软件安装---Tomcat安装

安装Tomcat 操作步骤&#xff1a; 使用xftp上传工具将tomcat的 二进制发布包上传到Linux解压安装包&#xff0c;命令为tar -zxvf apache-tomcat*** -C /usr/local进入Tomcat的bin的启动目录&#xff0c;命令为sh startup.sh或者./startup.sh 验证Tomcat启动是否成功&#xff0…

LeetCode:376. 摆动序列——说什么贪心和动规~

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;376. 摆动序列 题目描述&#xff1a;如果连续数字之间的差严格地在正数和…

Python 小型项目大全 46~50

# 四十六、百万骰子投掷统计模拟器 原文&#xff1a;http://inventwithpython.com/bigbookpython/project46.html 当你掷出两个六面骰子时&#xff0c;有 17%的机会掷出 7。这比掷出 2 的几率好得多&#xff1a;只有 3%。这是因为只有一种掷骰子的组合给你 2&#xff08;当两个…

「 分布式技术 」一致性哈希算法(Hash)详解

「 分布式技术 」一致性哈希算法&#xff08;Hash&#xff09;详解 参考&鸣谢 一致性 Hash 算法原理总结 kylinkzhang&#xff0c;腾讯 CSIG 后台开发工程师 什么是一致性哈希&#xff1f; xiaolinCoding 文章目录「 分布式技术 」一致性哈希算法&#xff08;Hash&#xff…

imagenet val 按类别分类

前言 有时候想看imagenet下某个类别的效果&#xff0c;但它又没划分… 之前看了这篇文章将ImageNet的验证集val数据分类到不同文件夹中&#xff0c;但不是很清楚那代码。 本文基于它的代码去做更改 把这个下下来 https://raw.githubusercontent.com/soumith/imagenetloader.…

ChatGPT 有哪些神奇的使用方式?

在遇到 ChatGPT之前&#xff0c;我很难想象&#xff0c;仅仅不到30s就能做出一个PPT。 而且对于小白来说&#xff0c;这个PPT绝对是「有水准、能拿得出手」的那种。 下面就是我用ChatGPTMindShow做的一套以分享短视频玩法为主题的 PPT&#xff0c;我挑几页大家看一下。 上面这…

10.Java面向对象----继承

Java面向对象—继承 面向对象简称 OO&#xff08;Object Oriented&#xff09;&#xff0c;20 世纪 80 年代以后&#xff0c;有了面向对象分析&#xff08;OOA&#xff09;、 面向对象设计&#xff08;OOD&#xff09;、面向对象程序设计&#xff08;OOP&#xff09;等新的系统…

2023 Java 面试题之MyBatis篇

持续更新内容涵盖&#xff1a;Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、 Linux 等技术栈&#xff08;滴滴滴.会持续更新哦&#xff0c;记得点赞、关注、分享三连击哈&#xff09;. My…

NumPy 秘籍中文第二版:十、Scikits 的乐趣

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们将介绍以下秘籍&#xff1a; 安装 scikit-learn加载示例数据集用 scikit-learn 对道琼斯股票进行聚类安装 Statsmodels使用 Statsmodels 执行…

【linux】——引导过程与服务控制

文章目录1.linux操作系统引导过程1.1 引导过程总览1.2 linux操作系统的引导过程1.3 系统初始化进程1.4 Systemd单元类型1.5 运行级别所对应的systemd目标2.排除启动类故障2.1 修复MBR扇区故障2.2 实例&#xff1a;修复MBR扇区故障2.2 修复GRUB引导故障2.3 实例&#xff1a;恢复…

电子数据取证(一)

电子数据取证概述 一&#xff0c;什么是电子数据 电子数据的特点 **1、以数字化形式存在。**所有的电子数据都是基于计算机应用和通信等电子化技术手段形成的&#xff0c;用以表示文字、图形符号、数字、字母等信息的资料。与其他证据种类不同&#xff0c;电子数据在本质上而…

Perpetuumsoft OLAP ModelKit .NET CRACK

关于 OLAP ModelKit 专业版 可视化您的数据透视表数据。OLAP ModelKit 是用 C# 编写的 .NET 多功能 OLAP 组件&#xff0c;仅包含 100% 托管代码。它具有 XP 主题外观和使用任何 .NET 数据源&#xff08;ADO.NET 和 IList&#xff09;的能力。通过在任何第三方组件&#xff08;…

java 面试消息题1-13

1. Redis 线程模型&#xff0c; 及为什么redis 这么快&#xff1f; 1.Redis虽然是一条一条处理命令的&#xff08;单线程&#xff09;&#xff0c;但是redis把每一条命令分成了很多个小命令&#xff0c;对这些小命令是多线程执行的。 2. IO 多路复用 - 可以用别人用过的IO。 …

RK3568平台开发系列讲解(调试篇)Oops 日志分析

🚀返回专栏总目录 文章目录 一、OOPS 日志分析二、OOPS 上的跟踪转储三、使用 objdump 识别内核模块中的错误代码行沉淀、分享、成长,让自己和他人都能有所收获!😄 📢编写代码并不总是内核开发中最难的方面。 调试是真正的瓶颈,即使对于经验丰富的内核开发人员也是如此…

Java同学入职环境安装全讲解

一、简述 最近入职一家新公司&#xff0c;拿到新电脑&#xff0c;那肯定有绕不开的装开发环境流程。下面我就从安装jdk、maven、git、idea四个方面讲解&#xff08;主要提供各个软件官方的下载网址&#xff0c;因为百度搜出来的东西大家懂的都懂我就不多说了&#xff09;。如果…

windows下安装虚拟环境保存在C盘之解决方案

正常在研究深度学习算法模型时&#xff0c;会现去创建一个配置软件包的虚拟环境&#xff0c;在windows下&#xff0c;一般新建的虚拟环境保存在Anaconda的安装目录下&#xff0c;比如我的安装目录是在 D:\program\Anaconda3\,然后虚拟环境将安装在anaconda中的envs文件夹中&…