2020-12-11 12章:存储类别,链接和内存管理

CPP 12章:存储类别,链接和内存管理

12.1存储类别

12.1.1作用域

  • 作用域描述程序中可访问标识符的区域。一个c变量的作用域可以是块作用域函数作用域函数原型作用域文件作用域是用花括号括起来的代码区域。
  • 如下面代码中的变量cleo和patrick都具有块作用域
double blocky(double cleo)
{
	double patrick=0.0;
	...
	return patrick;
}
  • 声明在内层块中的变量,其作用域仅限于该声明所在的块
double blocky(double cleo)
{
	double patrick=0.0;
	int i;
	for(i=0;i<10;i++)
	{
		double q=cleo*i;  //q的作用域的开始
		...
		patrick *= q;  //q的作用域的结束
	}
	return patrick;
}
  • 文件作用域。具有文件作用域的变量,从它的定义开始到该定义所在文件的末尾均可见。如:
#include <stdio.h>
int i=5;   //该变量具有文件作用域
int main()
{
	...
}
void main()
{
	...
}

12.1.2链接

  • C变量有三种链接属性:外部链接,内部链接,或无链接。
  • 具有块作用域,函数作用域或函数原型作用域的变量是无链接变量
  • 具有文件作用域的变量可以是外部链接或内部链接
int giants=5;           //文件作用域,外部链接
static int dodgers=3;   //文件作用域,内部链接
int main()
{
	...
}

12.1.3存储期

  • C语言有四种存储期:静态存储期,线程存储期,自动存储期,动态分配存储期。
  • 如果对象具有静态存储期,那么它在程序的执行期间一直存在。文件作用域变量具有静态存储期。关键字 static表明了其链接属性,而非存储期。以 static声明的文件作用域变量具有内部链接。但是无论是内部链接还是外部链接,所有的文件作用域变量都具有静态存储期。
  • 线程存储期用于并发程序设计,程序执行可被分为多个线程。具有线程存储期的对象,从被声明时到线程结束一直存在。
  • 块作用域的变量通常都具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。

12.1.4自动变量

  • 属于自动存储类别的变量具有自动存储期,块作用域且无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。
  • 关键字auto是存储类别说明符。
#include <stdio.h>
int main()
{
	int x=30;  //原始的x
	printf("...");
	int x=77;  //新的x,隐藏了原始的x
	printf("...");
	while(x++<33)  //原始的x 
	{
		int x=100; //新的x,隐藏了原始的x 
		x++;
		printf("...");
	}	
	printf("...");
	return 0;
} 
  • 自动变量的初始化
int main()
{
	int repid;
	int tents=5;
  • 此处tents变量被初始化为5,但是repid变量的值是之前占用分配给repid的空间中的任意值(如果有的话),别指望是0。

12.1.5寄存器变量

  • 寄存器变量存储在cpu的寄存器中,或者可以说,存储在最快可用的内存中。使用存储类别说明符register便可以声明寄存器变量。

12.1.6块作用域的静态变量

  • 静态变量的意思是变量在内存中原地不动。具有文件作用域的变量自动具有(也必须是)静态存储期。可以以存储类别说明符static声明这种变量。
#include <stdio.h>
int fun()
{
	int fade=1;
	static int stay=1;
} 
int main()
{
	...
}
  • 第一条声明每次调用函数都会执行这条声明。这是运行行为。
  • 第二条声明实际上不是函数的一部分,如果逐步调试该程序,会发现似乎跳过了这条声明。这是因为静态变量和外部变量在程序被载入内存是已经执行完毕。
  • 另外在函数的形参中不能使用static。

12.1.7外部链接的静态变量

  • 外部链接静态变量具有文件作用域,外部链接和静态存储期。
  • 为了指出该函数使用外部变量,可以在函数中使用关键字extern声明。
#include <stdio.h>
int errupt;			//外部定义的变量 
double up[100];     //外部定义的数组
extern char coal;   //如果coal被定义在另一个文件,则必须这样声明 
void next(void) 
int main()
{
	extern int errupt;  //可选的声明 
	extern double up[]; //可选的声明 
} 
void next(void)
{
	...	
} 

12.1.8内部链接的静态变量

  • 该存储类别具有静态存储期,文件作用域,和内部链接。用存储类别说明符static定义的变量具有这种存储类别.
static int svil=1;  //静态变量,内部链接
int main(void)
{

12.1.9多文件

  • 复杂的C程序通常由多个翻译单元组成,有时,有些文件可能要共享同一个变量。C语言通过在文件中进行定义式声明,然后在其他文件中引用来实现共享。也就是说,除了定义声明外,其他声明首要用extern关键字。而且,只有定义式声明才能初始化变量。

12.1.10存储类别说明符

  • auto: 表明变量具有自动存储类型
    auto说明符只能用在具有代码块作用域的变量的声明中, 但是由于这类变量本身就具有自动存储类型(存储于运行时堆栈中), 所以auto通常只是起显式说明的作用.

  • register: 表明变量具有硬件寄存器存储类型
    register也只能用在具有代码块作用域的变量的声明中, 表示程序员希望将该变量放在CPU的寄存器中, 从而可以比普通变量更快的访问和操作该变量. 但是无法获得寄存器存储类型的变量的地址, 并且具体是否会将register声明的变量存放于寄存器中由编译器决定.

  • static:表明变量具有静态存储类型或则标识符具有内部链接属性
    static用于具有代码块作用域的变量的声明时, 使该变量具有静态存储类型(存储于静态内存中).
    static用于具有文件作用域的标识符(变量名, 函数名)时, 表明该标识符具有内部链接属性.

  • extern: 表明标识符具有外部链接属性或者该变量在别处定义
    想要理解extern的用法首先要理解C语言的声明与定义的区别:
    声明: 描述 程序其他地方定义的对象
    定义: 为对象 分配内存

  • typedef
    typedef说明符不会为对象预留存储空间, 将其归类到存储类说明符只是为了语法描述方便.

12.1.11存储类别和函数

  • 函数也有存储类别,可以是外部函数(默认)或静态函数。
double gamma(double);  //默认为外部函数
static double beta(int ,int );
extern double delta(double,int );

12.2随机数函数和静态变量

12.3掷骰子

12.4分配内存:malloc()和free()

12.4.1 free()

  • 静态内存的数量在编译时是固定的,在程序运行期间也不会改变。自动变量使用的内存在程序执行期间自动增加或减少。但动态分配的内存只会增加,除非用free函数释放。
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
	int *p = malloc(sizeof *p);
	*p = 10;
	printf("p = %d\n", p);
	free(p);
	printf("p = %p\n", p);
	return 0;
}

12.4.2 calloc()函数

  • 分配内存还可以使用calloc()
long * newmem;
newmem = (long *)calloc(100, sizeof (long));
  • 和malloc()类似,在ANSI之前,calloc()也返回指向char的指针;在ANSI之后,返回指向void的指针。如果要储存不同的类型,应使用强制类型转换运算符。calloc()函数接受两个无符号整数作为参数(ANSI规定是size_t类型)。第1个参数是所需的存储单元数量,第2个参数是存储单元的大小(以字节为单位)。在该例中,long为4字节,所以,前面的代码创建了100个4字节的存储单元,总共400字节。

12.5 ANSI C类型限定符

12.5.1 const类型限定符

  • 声明普通变量和数组时使用 const 关键字很简单。指针则复杂一些,因为要区分是限定指针本身为const还是限定指针指向的值为const。下面的声明:
const float * pf; /* pf 指向一个float类型的const值 */
  • 创建了 pf 指向的值不能被改变,而 pf 本身的值可以改变。例如,可以设置该指针指向其他 const值。其与float const * pfc;相同。相比之下,下面的声明:
float * const pt; /* pt 是一个const指针 */
  • 创建的指针pt本身的值不能更改。pt必须指向同一个地址,但是它所指向的值可以改变。下面的声明:
const float * const ptr;

12.5.2类型限定符

  • volatile 限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常,它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。
  • volatile的语法和const一样:
volatile int loc1; // loc1 是一个易变的位置
volatile int * ploc; // ploc 是一个指向易变的位置的指针

12.5.3 restrict类型限定符

  • restrict 关键字允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。
int ar[10];
int * restrict restrict_ar = (int *) malloc(10 * sizeof(int));
int * par = ar;
  • 这里,指针restrict_ar是访问由malloc()所分配内存的唯一且初始的方式。 因此,可以用 restrict 关键字限定它。 而指针par既不是访问ar数组中数据的初始方式,也不是唯一方式。所以不用把它设置为restrict。

12.5.4 Atomic类型限定符(C11)

  • 并发程序设计把程序执行分成可以同时执行的多个线程。这给程序设计带来了新的挑战,包括如何管理访问相同数据的不同线程。C11通过包含可选的头文件stdatomic.h和threads.h,提供了一些可选的(不是必须实现的)管理方法。值得注意的是,要通过各种宏函数来访问原子类型。当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。
_Atomic int hogs; // hogs 是一个原子类型的变量
atomic_store(&hogs, 12); // stdatomic.h中的宏
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值