存储类别,链接和内存管理
1,作用域:
根据作用域可分为块作用域,函数作用域,函数原型作用域,文件作用域。
块作用域:在{}之间都一个属于块作用域。块里面定义的变量,作用范围是从定义处到包含该定义的块的末尾。
函数作用域:
函数原型作用域:函数原型作用域是从形参定义处到原型声明结束,
int mighty(int mouse,double large);//里面的mouse,large都属于函数原型作用域
文件作用域:变量的定义在函数外面,具有文件作用域,作用域为:从它定义处到该定义所在文件的末尾
2,链接:
链接主要分为:外部链接,内部链接,无链接;
无链接:具有块作用域,函数作用域或函数原型作用域的变量都是无链接变量。这意味着这些变量属于定义它们的块,函数或原型私有。
内部链接:内部链接变量即在该文件内部使用。使用static进行修饰,一旦变量使用static修饰后,外部文件将无法访问,只有文件内部才能够进行访问。
外部链接:外部链接就是跨文件访问,使用extern进行修饰,一般出现在多文件程序中。
3,存储期:
存储类别 | 存储期 | 作用域 | 链接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无 | 块内 |
寄存器 | 自动 | 块 | 无 | 块内,使用关键字register |
静态外部链接 | 静态 | 文件 | 外部 | 所有函数外 |
静态内部链接 | 静态 | 文件 | 内部 | 所有函数外,使用关键字static |
静态无链接 | 静态 | 块 | 无 | 块内,使用关键字static |
4,变量:
自动变量:
属于自动存储类别的变量具有自动存储期,块作用且无链接。默认情况下,
声明在函数头中的任何变量都属于自动存储类别。
int loop(int a)
{
int m;//m的作用域
scanf("%d",&m);
{
int i;//m和i的作用域
for(i=m;i<n;i++)
puts("i is local to a sub-block\n")
}
return m;//m作用域,i作用域已消失
}
寄存器变量:
寄存器变量是储存在CPU的寄存器上,这样的变量访问速度快,因储存在寄存器上,所以无法获取其地址,通常要申请这样的变量要使用关键字register
即:register int n;
块作用域的静态变量:
静态的意思是变量在内存中原地不动,并不是说它的值不变。具有文件作用域的变量自动具有静态存储期。前面提到过,可以创建具有静态存储期、块作用域的局部变量。这些变量和自动变量一样,具有相同的作用域,但是程序离开它们所在的函数后,这些变量不会消失。也就是说,这种变量具有块作用域、无链接,但是具有静态存储期。计算机在多次函数调用之间会记录它们的值。在块中(提供块作用域和无链接)以存储类别说明符 static(提供静态存储期)声明这种变量。
/** 使用局部静态变量 */
#include <stdio.h>
void trystat(void);
int main()
{
int count;
for(count = 1; count <= 3; count++)
{
printf("Here comes iteration %d:\n",count);
trystat();
}
return 0;
}
void trystat(void)
{
int fade = 1;
static int stay = 1;
printf("fade = %d and stay = %d\n",fade++,stay++);
}
该程序的输出如下:
Here comes iteration 1:
fade = 1 and stay = 1
Here comes iteration 2:
fade = 1 and stay = 2
Here comes iteration 3:
fade = 1 and stay = 3
静态变量只在编译时初始化一次;
不能在函数参数中用static
外部链接的静态变量:
外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别,属于该类别的变量称为外部变量。把变量的定义性声明放在在所有函数的外面便创建了外部变量。当然,为了指出该函数使用了外部变量,可以在函数中用关键字 extern 再次声明。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用 extern 在该文件中声明该变量
#include <stdio.h>
int Errupt; /* 外部定义的变量 */
double Up[100]; /* 外部定义的数组 */
extern char Coal; /* 如果 Coal 被定义在另一文件,则必须这样声明 */
void next(void);
int main()
{
extern int Errupt; /* 可选的声明 */
extern double Up[]; /* 可选的声明 */
return 0;
}
void next(void)
{
...
}
初始化外部变量时不能用变量初始化:int x=2*n
文件中任意函数均可使用外部变量;其他文件使用时必须用关键字extern
来引用定义
内部链接的静态变量:
该存储类别的变量具有静态存储期、文件作用域和内部链接。在所有函数外部(这点与外部变量相同),用存储类别说明符 static 定义的变量具有这种存储类别。
*它与外部链接的静态变量不同于他只能用于该文件的任意函数
5,存储类别和函数:
函数也有存储类别,可以是外部函数(默认)或静态函数。C99 新增了第 3 种类别——内联函数。外部函数可以被其他文件的函数访问,但是静态函数只能用于其定义所在的文件。假设一个文件中包含了以下函数原型:
double gamma(double); /* 该函数默认为外部函数 */
static double beta(int, int);
extern double delta(double, int);
在同一个程序中,其他文件中的函数可以调用 gamma() 和 delta(),但是不能调用 beta(),因为 static 存储类别说明符创建的函数属于特定模块私有。这样做避免了名称冲突的问题,由于 beta() 受限于它所在的文件,所以在其他文件中可以使用与之同名的函数。
通常的做法是:用 extern 关键字声明定义在其他文件中的函数。这样做是为了表明当前文件中使用的函数被定义在别处。除非使用 static 关键字,否则一般函数声明都默认为 extern。
6,分配内存:malloc()和free()
malloc可以申请内存,它会找到合适的空闲内存块,这样的内存是匿名的,它不同于普通的内存分配,它可以动态分配内存,通常申请内存形式如下:
double pta;
pta=(double) malloc(30*sizeof(double))
也可以将30改为变量来分配内存;
/** 动态分配数组 */
#include <stdio.h>
#include <stdlib.h> /* 为 malloc()、free() 提供原型 */
int main()
{
double * ptd;
int max;
int number;
int i = 0;
puts("What is the maximum number of type double entries?");
if(scanf("%d", &max) != 1)
{
puts("Number not correctly entered -- bye.");
exit(EXIT_FAILURE);
}
ptd = (double *) malloc(max * sizeof(double));
if(ptd == NULL)
{
puts("Memory allocation failed. Goodbye.");
exit(EXIT_SUCCESS);
}
/* ptd 现在指向有 max 个元素的数组 */
puts("Enter the values (q to quit):");
while(i < max && scanf("%lf", &ptd[i]) == 1)
++i;
printf("Here are your %d enteries:\n", number = i);
for(i = 0; i < number; i++)
{
printf("%7.2f", ptd[i]);
if(i % 7 == 6)
putchar('\h');
}
if(i % 7 != 0)
putchar('\n');
puts("Done.");
free(ptd);
return 0;
}
下面是该程序的运行示例。程序通过交互的方式让用户先确定数组的大小,我们设置数组大小为 5。虽然我们后来输入了 6 个数,但程序也只处理前 5 个数。
What is the maximum number of type double entries?
5
Enter the values (q to quit):
20 30 35 25 40 80
Here are your 5 enteries:
20.00 30.00 35.00 25.00 40.00
Done.
在使用了malloc函数后一定要记得free()
静态内存的数量在编译时是固定的,在程序运行期间也不会改变。自动变量使用的内存数量在程序执行期间自动增加或减少。但是动态分配的内存数量只会增加,除非用 free() 进行释放。
假设有个循环要执行 1000 次,每次分配 16000字节的内存,所以在循环结束时,内存池中有 1600 万字节被占用。实际上,也许在循环结束之前就已耗尽所有内存。这类问题被称为内存泄漏(memory leak)。在函数末尾处调用 free() 函数可避免这类问题发生。
7,const限定符:
const限定符修饰的变量不能改变其值:
const float * p; //p可以指向别处,p指向的值不能变
float * const p; //p不可以指向别处,p指向的值可以改变
const float * const p; //p不可以指向别处,p指向的值不可以改变
const int arr[]; //不能改变arr中的数据
const int * arr; //同上
2、对全局数据使用 const
使用全局变量是一种冒险的方法,因为这样做暴露了数据,程序的任何部分都能更改数据。如果把数据设置为 const,就可避免这样的危险,因此用 const 限定符声明全局数据很合理。可以创建 const 变量、const 数组和 const 结构。
然而,在文件间共享 const 数据要小心。可以采用两个策略。
第一,遵循外部变量的常用规则,即在一个文件中使用定义式声明,在其他文件中使用引用式声明(用 extern 关键字)
第二,把const变量放在一个头文件中;