《c primer plus》c语言学习笔记整理(十二)-存储类别、链接和内存管理

第十二章 存储类别、链接和内存管理

1.存储类别

(1)作用域
1)块作用域:局部变量(包括函数形式参数)都具有块作用域,变量可见范围是从定义处到包含该定义的块的末尾,定义在块中的变量具有块作用域
2)函数作用域:仅用于goto语句。
3)函数原型作用域:用于函数原型中的形参名(变量名),函数原型作用域的范围是从形参定义处到原型声明结束。
4)文件作用域:具有文件作用域的变量从定义处到该定义所在文件的末尾均可见。变量的定义在函数外面。
(2)链接
1)外部链接:具有文件作用域的变量
2)内部链接:具有文件作用域的变量
3)无链接:具有块作用域、函数作用域、函数原型作用域的变量
注:文件作用域:内部链接的文件作用域;
"全局作用域"或“程序作用域”:外部链接的文件作用域
(3)存储期
作用域和链接描述了标识符的可见性。存储期描述了通过这些标识符访问的对象的生存期。
1)静态存储期:在程序执行期间一直存在。对于文件作用域变量,关键字static表明了其链接属性,而非存储期,以static声明的文件作用域变量具有内部链接。
2)线程存储期:用于并发程序设计,程序执行被分为多个线程。具有线程存储期的对象,从被声明到线程结束时一直存在。以关键字_Thread_local声明一个对象时,每个进程都获得该变量的私有备份。
3)自动存储期:块作用域的变量通常具有自动存储期。进入定义变量的块时,分配内存,退出块时,释放内存。
4)动态分配存储期
注:块作用域变量也可以具有静态存储期,变量声明在块中,前面加上关键字static。
(4)自动变量
1)默认情况下,声明在块或者函数头中的任何变量都属于自动存储类别。
2)auto关键字在C++中用法完全不同,如果编写C/C++兼容的程序,最好不要使用auto作为存储类别说明符。
3)自动变量的初始化:可以用非常量表达式初始化自动变量,前提是所用的变量前面已经定义过。
4)如果内层块中生命的变量与外层块中变量同名,内层块会隐藏外层块的定义。
5)作为循环或if语句的一部分,即使不用花括号({}),也是一个块。整个循环是它所在块的子块,循环体是整个循环块的子块,与此类似,if语句是一个块,与其相关联的子语句是if语句的子块。
(5)寄存器变量
1)储存在最快的可用内存中,访问和处理这些变量速度更快
2)无法获取寄存器变量的地址
3)绝大多数方面,与自动变量一样,块作用域,无链接,自动存储期
4)使用存储类别说明符register便可声明。
5)可声明为register的数据类型有限(寄存器可能没足够大空间存储double类型的值)
(6)块作用域的静态变量(局部静态变量)
1)存储类别:内部静态存储类别(内部指的是函数内部)
2)静态的意思是该变量在内存中原地不动,并不是说值不变
3)具有文件作用域的变量自动具有(也必须是)静态存储期
4)这类变量和自动变量一样,具有相同的作用域,但是程序离开它们所在的函数后,这些变量不会消失(块作用域)
5)在块中(提供作用域和无链接)用以存储类型说明符static(提供静态存储期)声明这种变量。
(7)外部链接的静态变量
1)具有文件作用域、外部链接、静态存储期
2)把变量的定义性声明放在所有函数的外面便创建了外部变量
3)为了指出该函数使用了外边变量,可以在函数中用关键字extern再次声明。
4)如果一个源代码文件使用的外部变量定义在另一个源代码文件中, 则必须用extern在该文件中声明该变量。
5)执行块中的语句的时候,块作用域中的变量将“隐藏”文件作用域中的同名变量,如果不得已要使用与外部变量同名的局部变量(可以在局部变量的声明中使用auto存储类别说明符明确明确表明这种意图)
6)外部变量作用域:从声明处到文件结尾
7)初始化变量:与自动变量不同,如果未初始化外部变量,他们会被自动初始化为0。这一原则也适用于外部定义的数组元素。与自动变量情况不同,只能使用常量表达式初始化文件作用域变量
8)不要用关键字extern创建外部定义,只用它来应用现有的外部定义
9)外部变量只能初始化一次,且必须在定义该变量时进行。
10)外部名称:C99和C11标准都要求编译器识别局部标识符的前63个字符和外部标识符的前31个字符。(外部变量名规则更加严格,因为其遵循局部环境规则,所受的限制更多)
(8)内部链接的静态变量
1)具有静态存储期、文件作用域、内部链接
2)在所有函数的外部(这点和外部变量相同)用存储类别说明符static定义的变量具有这种存储类别
(9)多文件
1)C通过在一个文件中进行定义式声明, 然后再其他文件中进行引用式声明来实现共享,也就是除了一个定义式声明外,其他声明都要使用extern关键字。
2)只有定义式声明才可以初始化变量
3)如果外部变量定义在一个文件中,那么其他文件在使用该变量之前必须先声明它(用extern关键字)
(10)存储类别说明符
(11)存储类别和函数
1)函数也有存储类别,可以是外部函数(默认)或静态函数,C99增加了第3种类别-内联函数
2)外部函数可以被其他文件的函数访问,但是静态函数只能用于其定义所在的文件。
3)通常做法:用extern关键字声明定义在其他文件的函数。这样做是为了表明当前文件中使用的函数被定义在别处。除非使用static关键字,否则一般函数声明都默认为extern。
(12)存储类别的选择
1)C语言有6个关键字作为存储类别说明符:auto、register、static、extern、_Thread_local和typedef。
2)在绝大多数情况下,不能在声明中使用多个存储类别说明符,唯一的例外是_Thread_local,可以和extern或static一起使用。
3)auto:表明变量是自动存储类别,只能用于块作用域的变量声明中。由于在块中声明的变量本身就具有自动存储期,所以使用auto主要是为了明确表达要使用与外部变量同名的局部变量的意图。
4)register:也只用于块作用域的变量
5)static:具有静态存储器的对象,载入程序时创建对象,程序结束后对象消失。用于文件作用域的声明,作用域受限于该文件;用于块作用域声明,作用域受限于该块。
6)extern:表明声明的变量定义在别处。如果包含extern的声明具有文件作用域,则引用的变量必须具有外部链接;如果包含extern的声明具有块作用域,引用的变量可能具有外部链接或内部链接,这接取决于该变量的定义式声明。
1)绝大多数选择”自动存储类别“,这是默认类型
2)保护性程序设计的环境原则:“按需知道”原则。尽量在函数内部解决该函数的任务,只共享那些需要共享的变量。
3)在使用其他类别之前要考虑下是否有必要这么做。
2.随机数函数和静态变量
(1)使用内部链接的静态变量的函数:随机数函数
(2)自动重置种子
1)如果c实现允许访问一些可变的量(如,时钟系统),可以用这些值(可能被截断)初始化种子值,time.h,time()返回值的类型名为time_t,具体类型与系统有关,time()接受的参数是一个time_t类型的地址,而时间值就存储在传入的地址上
(3)
3.分配内存malloc()和free()
(1)malloc()函数:
1)接受一个参数-所需的内存字节数。会找到合适的空闲内存块,这样的内存块是匿名的,malloc()分配内存,但是不会为其赋名,返回动态分配内存的首字节地址。可以把该地址赋给一个变量,并使用指针访问这块内存
2)从ANSI C标准开始,C使用一个新的类型:指向void的指针。该类型相当于一个“通用指针”,可用于返回数组指针、指向结构的指针;所以通常该函数的返回值会被强制转换为匹配的类型。把指向void的指针赋给任意类型的指针完全不考虑类型匹配的问题。
3)我们用malloc()创建一个数组的时候,除了用malloc()在程序运行时请求一块内存,还需要一个指针记录这块内存的位置。
4)可以使用数组名表示指针,也可以用指针表示数组
(2)free()的作用
1)malloc通常要和free配套使用,free()函数参数是之前malloc()返回的地址,该函数释放之前malloc()分配的内存。free函数只释放其参数所指向的内存块。
2)使用malloc()函数,程序可以在运行时才确定数组大小
3)malloc()可能分配不到所需的内存,在这种情况下,该函数返回空指针,程序结束。
4)为了保险起见,请使用free()释放malloc()函数分配的内存,不要依赖操作系统来清理。在函数末尾处调用free()函数可以避免内存泄漏
(3)calloc()函数
1)与malloc类似,ANSI之前,返回指向char的指针,之后,返回指向void的指针。
2)接收两个无符号整数作为参数(ANSI规定是size_t类型)。第一个参数为所需存储单元数量,第二个参数为存储单元大小(以直接为单位)
3)特性:把块中所有位都设置为0
4)free()函数也可用于calloc()分配内存的释放
(4)动态内存分配与变长数组
1)区别:变长数组是自动存储类别,另一方面,用malloc()函数创建的数组不必局限于一个函数内访问(比如malloc()创建数组,由free()这另一个函数释放,free()所用的指针变量可以和malloc()的指针变量不同,但是两个指针必须储存相同的地址),但是不可以释放同一块内存两次。
2)对于多维数组,使用变长数组更加方便,当然也可以用malloc()创建二维数组,但是语法比较繁琐。
(5)存储类别与动态内存分配
程序把可用内存分为3部分:
1)静态存储类别变量使用的内存:所用的内存数量在编译时确定,只要程序还在运行,就可以访问储存在该部分的数据。该类别的变量在程序开始执行时被创建,在程序结束时被销毁。
2)自动存储类别变量使用的内存:这部分内存通常作为栈来处理,新创建的变量按顺序加入内存,然后以相反的顺序销毁。
3)动态分配的内存:内存块可以在一个函数内创建,另一个函数内销毁,这部分的内存用于动态内存分配会支离破碎,未使用的内存块分散在已使用的内存块之间,且使用动态内存通常比栈慢。
4.ANSI C类型限定符
(1)const类型限定符
(2)volatile类型限定符
(3)restrict类型限定符
(4)_Atomic类型限定符(C11)
(5)旧关键字的新位置

猜你喜欢

转载自blog.csdn.net/weixin_45096408/article/details/104758689