C primer Plus study note chapter 12

存储类别:C中提供了许多不同的模型或者存储类别在内存中存储数据。
从硬件方面看:被存储的值都占用了一定的物理内存,C中把这样的一块内存称为对象
对象可以存储一个或者多个值,一个对象可能并未存储实际的值,但是它在储存适当的值时一定具有相应的大小
可以用存储期描述对象,所谓的存储期是指对象在内存中保留了多长时间,标识符用于访问对象,可以用作用域和链接描述标识符,标识符的作用域和链接表明了程序的哪些部分可以使用它
不同的存储类具有不同的存储期和作用域还有链接,标识符可以在源代码的多文件中共享,可以用于特定的文件的任意函数中,也可以仅限与特定的函数中,甚至只在函数中的某部分中使用,
对象可以存在于程序的执行期,也可以仅存在与它所在的函数的执行期,对于并发编程,duiixang可以在特定线程的执行期中存在,可以通过函数的额调用的方式显式分配和释放内存。

作用域:
描述程序中可以访问标识符的区域一个C变量的作用域可以是块作用域,也可以是函数作用域、函数原型作用域、或者文件作用域。块是一对用花括号括起来的代码区域。
块作用域的可见范围是从定义处到该定义的块的末尾
函数作用域:仅用于goto语句的标间,这意味着即使一个标签首次出现在函数的内层块中,他的作用域也延伸到整个函数,如果在两个块中使用子的标签会很混乱,
标签的额函数作用域就算为了防止这种情况。
函数原型作用域:用于函数原型中的形参名(变量名)
函数原型作用域的范围是从形参定义处到原型声明结束
变量定义在函数的外面,具有文件作用域,具有完呢见作用域的变量,从它的定义处到该定义所在的完呢见的末尾均可见。同时也称呼为全局变子

链接:C中变量有3中链接属性
外部链接:可以在多文件程序中使用
内部链接:内部链接只能在一个翻译单元中使用
无链接:,具有块作用域、函数作用域或者函数原型作用域的变量都是无链接变量。

存储期:作用域和链接描述了标识符的可见性,存储期通过这些标识符放那该文对象的生存期,C对象有4种存储期:静态存储期,线程存储期,自动存储期,动态分配存储期。
如果对象具有静态存储期,那么它在程序的执行期间一直存在,完呢见作用域变量具有静态存储期,注意对于文件作用域变量,关键字static表明了其链接属性,
而非存储期,以static声明的文件作用域变量具有内部链接,
但无论是内部还是外部链接,所有的文件作用域变量都具有静态存储期。
线程存储期用于并发程序设计
块作用域变量通常具有自动存储其,当程序进入这些变量的块时,为这些变量自动分配内存。,当退出这个块时,则自动把这些内存释放。到目前为止局部变量都是使用的自动变量。

自动变量:属于自动存储类别的变量具有自动存储期、块作用域且无链接。关键字auto是存储类别说明符,但是在C++中auto的用法完全不同。
块作用域和无链接意味着只有在变量定义的块中才能通过变量名访问该变量另一个函数可以使用同名变量,但是该变量是存储在不同内存位置的另外一个变量。

寄存器变量:寄存器变量存储在CPU中,或者概括说,存储在最快可用的内存中,与普通变量相比,访问和处理速度更快,但是由于其是存储在寄存器而非内存中,所以无法或其寄存器的地址,
绝大多数方面,寄存器变量和自动变量是一样的,可以使用register类别说明符来声明寄存器变量。
寄存器变量的声明不一定会成功,看最快可用内存的数量来决定,如果无法成功,变量还是声明成功了,但是却是普通变量,但是还是无法对其使用地址运算符。
静态变量的意思是该变量在内存中原地不动,值可以变化,但是内存中给这个变量分配的这一块内存会一直不变具有文件作用域的变量自动具有同时也必须是静态存储期。
前面提到过,可以创建具有静态存储其、块作用域的局部变量。
这些变量和自动变量一样,具有相同的作用域,但是程序离开执行块之后,这些变量不会消失,也就是说这种变量具有块作用域、无链接,但那是具有静态存储期。以static来声明这种变量。
局部静态变量,是描述具有块作用域的静态变量的另外一个术语,它的这种存储类别又被称呼为,内部静态存储类别,这里指的是函数内部。
外部链接的静态变量:
外部链接的静态变量具有文件作用域、外部链接和静态存储其,这种类别又被称呼为外部存储类别,他的变量叫外部变量,外部变量声明在所有函数的外面,为了在函数中使用外部变量,
可以使用extern在函数中再次声明
如果外部变量被声明在另外一个文件中,则需要在使用的文件中重新使用extern声明该变量。
外部变量和自动变量类似,也可以被显式初始化,,与自动变量不同的是,如果没有显式初始化,则它们会被自动初始化为0,这同样适用于外部数组元素,但是外部变量只能使用常量初始化文件作用域变量。

外部变量的使用:外部变量在函数中使用是可以重复声明,也可以不需要重复声明,所有函数中使用的同一个外部变量都是同一个定义在外部的变量。
内部链接的静态变量:该类别的变量具有静态变量存储期、外文见作用域和内部链接,在所有函数的外部用存储类别说明符static定义的变量具有这种存储类别。
当多文件编译时,如果共享一个外部变量的话,必须使用定义式声明声明该外部变量,在其余文件中使用时,必须使用引用式声明,就是说其余文件在使用这个外部变量时,
必须先使用extern关键字进行声明
存储类别说明符:
C中有6个存储类别说明符:auto,register,static,extern,_Thread_local和typedef
typedef和任何内存存储无关,——Thread_local可以和static和extern一切连用。
auto 说明符表明变量是自动存储期,只能用于块作用域的变量声明中,由于在块中声明的比那量本身就具有自动存储期,所以使用auto主要是为了明确表达要使用与外部变量同名的局部变量的意图
register说明符也只用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量,同时,还保护了该地址不被获取。
用static说明符chungking的对象具有静态存储期,载入程序时创建对象,当程序结束时对象消失,如果static说明符用于文件的作用域声明,作用域受限于该文件。
如果static用于块作用域的声明,作用域则受限于该块,因此,只要程序在,运行对象就存在并保留其值,但是只有在执行块内部的代码时,才能通过标识符访问该块,
块作用域的静态变量无链接,文件作用域的静态便来功能具有内部链接。
extern说明符表明声明的变量如果定义在别处,如果包含extern 的声明具有文件作用域,则引用的变量必须具有该外部链接,如果extern的声明具有块作用域,
则引用的变量可能具有外部链接或内部链接。
小结:
自动变量具有块作用域、无链接、自动存储期,它们是局部变量,属于其定义所在的块中私有,寄存器变量的属性和自动变量相同,
但编译器会使用更快的访问内存或则寄存器来存储它们,不能获取寄存器变量的地址。
具有静态存储期的对象可以具有外部链接、内部链接或者是无链接,在同一个文件的所有函数的外部声明的变量就算外部变量,具有,文件作用域、外部链接、静态存储期,
如果在这种变量前面加了static关键字声明,则该变量具有文件作用域、内部链接、静态存储期,如果函数中使用static声明一个变量则该变量具有块作用域、无链接、静态存储期
具有自动存储期的变量,程序在进入到该变量的声明所在块时,才给他分配内存,在退出该块时,则释放之前分配的内存,
如果未初始化,则内存中的自动变量是垃圾值,程序在编译时为具有静态存储期的变量分配内存,并在程序的运行过程中一直保留该内存,如果未初始化,这样的变量会被默认设置为0
具有块作用域的变量是局部变量,属于包含该变量的声明的块私有,具有文件作用域的变量对文件中位于其声明后面的所有函数可见,具有外部链接的文件作用域变量,可用于该程序的其他翻译单元
具有内部链接的文件作用域变量,只能用于其声明所在的文件内部。
通常的做法是:用extern关键字声明定义在其他文件中的函数,这样做是为了表明当前文件中使用的函数被定义在别处,chuffed使用static关键字,否则一般函数声明都默认为extern。
保护性程序设计的法则是:按需知道原则,尽量在函数内部解决该函数的任务,共享那些需要共享的变量,除自动存储类别外,其余存储类别也很有用,默认的存储类别就算自动存储类别

随机函数和静态变量:随机数函数是使用了内部链接的静态变量函数

分配内存:malloc()和free()
C中内存分配的主要工具是malloc()函数,该函数接收一个参数:所需的内存字节数。malloc()函数会找到适合空闲的内存块,这样的内存是匿名的。也就是说malloc()函数分配内存,但是不会为其赋名
然而,它确实返回动态内存分配的内存块的首字节地址,因此可以把该地址赋予一个指针,通过指针访问这块内存,
从ASNSI——C标准开始,malloc()函数返回void的指针,void的指针可以赋予任意类型的指针,完全不用考虑类型匹配的问题,如果malloc()分配内存失败,将返回一个空指针。
声明一个指针,使用malloc()函数,将其返回值赋给指针,使用指针访问数组元素,该指针可以是静态的,也可以是自动的,这相当于声明了一个变长数组、动态数组。
通常malloc()函数要和free()函数一起使用,free()函数的参数是,malloc()函数的返回的地址,free()函数的作用是用于释放之前malloc()函数分配的内存。
因此,动态分配的内存的存储其从调用malloc()分配内存到调用free()释放内存为止,
设想malloc()和free()管理着一个内存池,每次从调用mdlloc()分配内存给程序使用,到每次调用free()把内存归还内存池,这样可以重复使用这些内存,
free的参数是一个指针,指向由malloc一起管理分配的额内存,不能通过free释放通过其他方式分配的内存,malloc和free的原型都在stdlib.h中
使用malloc函数时,程序可以在运行时才确定数组的大小
free可以避免出现内存泄漏,内存泄漏指的是:比如一块1000K的内存,第一次函数时创建了一个指针,分配了10K的内存,函数执行完毕时,忘记是否那个内存,导致那块内存始终占用着,
然后第二次,调用函数,又创建了一个新的指针对象,分配了10K内存,函数中有个循环会循环100次,从而分配完1000K的内存,但是由于执勤已经有一块占用了10K的内存,
导致在循环结束之前就已经耗尽了所有的内存,这就是内存泄漏。
calloc()函数:分配内存还可以用calloc()函数,和malloc()函数类似,在ANSI之前返回char类型的指针,在ANSI之后返回void 类型的指针,,这个函数接收两个符号整数作为参数,
第一个参数是所需存储单元的数量,第二个采纳数是存储单元的大小。,可以用sizeof(int),这种形式作为第二个参数提高可移植性。
动态内存分配和变长数组:变长数组属于自动存储类型,,因此程序在离开代码所在块时,会自动释放变长数组的内存,不必使用free,而malloc函数分配的内存空间,必须使用free来释放。
另外,free函数所用的指针变量可以和malloc返回的指针变量不一样,但是两个指针中存储的必须是相同的地址,但是不能释放同一块内存两次。
存储类别和动态内存加固:
静态存储类别所用的内存数量在编译时确定,自动存储类别随函数的调用开始,函数的结束消失,,这部分通常作为栈内存处理
动态分配内存则在调用malloc或相关函数时分配在调用free后释放,这部分内存由程序员管理,所以这部分内存比较散碎,使用动态内存比使用栈内存慢。
const类型限定符:
在指针或者形参声明中使用const限定符:虚分清楚,限定的是指针对象,还是指针指向的对象
如:const float* p;//p指向一个float类型的const值 float * const p;//一个const的float类型指针p const float* const p;//const类型的指针指向const类型的对象
对函数的形参声明数组或指针时使用const限定符防止出现函数调用修改了原数据对象的情况。
对全局变量使用const,可以避免出现数据暴露;

volatile类型限定符:
这个限定符告诉计算机,代理可以改变该变量的值,它通常被用与硬件地址以及在其他程序或同时运行的线程中共享数据。
restrict限定符:
这个关键字允许编译器优化某部份代码,以便于更好地支持计算机。它只能用于指针,表明指针是访问数据对象的唯一且初始方式

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 几种语言的特性 汇编程序:将汇编语言源程序翻译成目标程序编译程序:将高级语言源程序翻译成目标程序解释程序:将高级语...
    囊萤映雪的萤阅读 8,016评论 1 5
  • 版权声明:本文为 gfson 原创文章,转载请注明出处。注:作者水平有限,文中如有不恰当之处,请予以指正,万分感谢...
    gfson阅读 8,559评论 0 6
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,793评论 1 32
  • 重新系统学习下C++;但是还是少了好多知识点;socket;unix;stl;boost等; C++ 教程 | 菜...
    kakukeme阅读 20,122评论 0 50
  • 通过 刚才与王鹏的话语中可以得出 :我的内心中 还是希望与人在一起的(当然呀!) 但是,我又很排斥与人“在一起” ...
    赵扬帆阅读 1,417评论 0 0