存储类别
C语言提供了多种不同的模型或存储类别在内存中存储数据。
作用域
作用域描述程序中可访问标识符的区域。先做简单介绍:
一个C变量的作用域可以是:
- 块作用域
- 函数作用域
- 函数原型作用域
- 文件作用域
块是用一对儿花括号括起来的代码区域。整个函数体是一个块,函数中的任意符合语句也是一个块。
例如:
int Add_Sum(int num)
{
int sum = 0;
for (int i = num; i >0; --i) //i是for循环这个块内的变量
{
sum += i;
} //i的作用域结束
return sum;
}
函数作用域仅用于goto语句的标签,这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸至整个函数。
函数原型作用域用于函数原型中的形参名
int Add_Sum(int num);
num的作用域从定义开始到原型声明结束。
变量的定义在函数的外面,具有文件作用域。
具有文件作用域的变量,从定义处到该定义所在文件的末尾均可见。
#include<stdio.h>
#include<stdlib.h>
int count = 0; //具有文件作用域
int main(void)
{
...
...
return 0;
}
像count这样的变量可用于多个函数,所以也被称为全局变量。
链接
C变量有三种链接属性:
外部链接;
内部链接;
无链接。
具有块作用域,函数作用域和函数原型作用域的都是无链接变量。
具有文件作用域的变量可以是外部链接或内部链接,
外部链接变量可以在多文件程序中使用,
内部链接变量只能在一个翻译单元中使用。
(翻译单元:编译器把源代码文件和所有的头文件都看成是一个包含信息的单独文件,这个文件被称为翻译单元。如果程序由多个源代码文件组成,那么该程序也将由多个翻译单元组成。每个翻译单元均对应一个源代码文件和它所包含的文件)
static
如何知道文件作用域变量是内部链接还是外部链接?
查看外部定义中是否使用了存储类别说明符static。
#include<stdio.h>
int Max = 10; //文件作用域,外部链接
static int Min = 0; //文件作用域,内部链接
int main(void)
{
...
}
该文件和统一程序的其他文件都可以使用变量Max, 而变量 Min属于文件私有,该文件中的任何函数都可以使用它。
存储期
作用域和链接描述了标识符的可见性,存储期描述了通过这些标识符访问的对象的生存期。
C对象有四种存储期:
- 静态存储——文件作用域变量具有静态存储期。
- 线程存储期——用于并发程序设计,程序执行可被分为多个线程。具有线程存储期的对象,从被声明时到线程结束一直存在。
- 自动存储期——块作用域的变量一般具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。
- 动态分配存储期
存储类别
5种存储类别
自动变量
属于自动存储类别的变量具有自动存储期,块作用域且无链接。
一般可以这样声明:
auto int x;
块作用域和无链接意味着只有变量在定义所在的块中才能通过变量名访问该变量。
例如:
#include<stdio.h>
int main(void)
{
int x = 10;
printf("x in outer block :%d at %p\n", x, &x);
if (x > 1)
{
int x = 20;
printf("x in inner block :%d at %p\n", x, &x);
}
return 0;
}
运行结果:
两个x的地址不一样。这就相当于二楼有个人叫张三,三楼也有个人叫张三,但他俩肯定不是同一个人。。
块作用域的静态变量
静态变量意思是该变量在内存中原地不动。程序在离开他们所在的函数后,这些变量不会消失。
示例:
#include<stdio.h>
void Try_Static();
int main(void)
{
int i = 0;
for (i = 0; i < 5; ++i)
{
Try_Static();
}
return 0;
}
void Try_Static()
{
int a = 0;
static int b = 0;
printf("a = %d \n", a++);
printf("b = %d \n", b++);
}
运行结果:
可以看到在每次调用函数Try_Static()时候,a都会重新声明,而b一直存在。
外部链接的静态变量
具有文件作用域,外部链接和静态存储期。
属于该类别的变量称为外部变量。
如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明变量。
#include<stdio.h>
int x; //外部定义的变量
int ar[10]; //外部定义的数组
extern int a; //如果a被定义在另一个文件中,则要这样声明
int main(void)
{
extern int x; //可以
...
return 0;
}
内部链接的静态变量
这种变量具有静态存储期,文件作用域和内部链接。
用存储类别说明符static定义在所有函数外部。
示例:
int a = 10; //外部链接
static int b = 10; //内部链接
int main(void)
{
extern int a; //使用定义在别处的a
extern int b; //使用定义在别处的b
}
a和b都具有文件作用域,但只有a可以用于其他翻译单元。
两个声明都使用了extern关键字,说明main()中使用的两个变量的定义都在别处。
程序示例
//part_a.cpp
#include<stdio.h>
void Report_Count();
void accumulate(int n);
int count = 0; //文件作用域,外部链接
int main(void)
{
int value; //自动变量
register int i; //寄存器变量
printf("Please enter a positive integer ");
printf("or enter q to quit:\n");
while (scanf_s("%d", &value) == 1 && value > 0)
{
++count;
for (i = value; i >= 0; --i)
{
accumulate(i);
}
printf("Please enter next poistive integer ");
printf("or enter q to quit:\n");
}
Report_Count();
return 0;
}
void Report_Count()
{
printf("Loop executed %d times\n", count);
}
//part_b.cpp
#include<stdio.h>
extern int count; //引用式声明,外部链接
static int total = 0; //静态定义,内部链接
void accumulate(int n);
void accumulate(int n)
{
static int subtotal = 0; //静态无链接
if (n <= 0)
{
printf("Loop cycle: %d\n", count);
printf("subtotal:%d; total: %d\n", subtotal, total);
subtotal = 0;
}
else
{
subtotal += n;
total += n;
}
}
运行示例:
随机数函数和静态变量
ANSI C提供了**rand()**函数生成随机数。
程序如下:
// rand0.cpp
//种子
static unsigned long int next = 1;
unsigned int rand0()
{
//生成伪随机数的公式
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
然后测试一下这个程序:
//测试
#include<stdio.h>
extern unsigned int rand0();
int main(void)
{
int ar[10];
//利用随机数初始化数组
for (int i = 0; i < 10; ++i)
{
ar[i] = rand0();
}
//打印出来查看
for (int i = 0; i < 10; ++i)
{
printf("%d\n", ar[i]);
}
return 0;
}
运行结果: