在c语言中,每一个变量和函数有两个属性:
数据类型
和数据的存储类别
。C语言中局部变量和全局变量变量的存储类别(
static
,extern
,auto
,register
)
1. 从变量的作用域划分变量(即从空间
)角度来分
1.全局变量
2.局部变量
2. 从变量值存在的时间或存储类别(即生存期
)角度来分
2.1. 静态存储区
存放下面数据:
代码段(text)
、仅仅读数据段(rodata)
、读写数据段(rwdata)
、未初始化数据段(bbs)
静态存储区存放所有的全局变量, 这些变量将在链接之后产生, 程序执行完成就释放, 程序执行的过程中它们占领固定的存储单元, 而不会动态的进行分配和释放
2.2. 动态存储区
存放下面数据:
函数形參
、自己主动变量(未加static声明的局部变量)
、函数调用时的现场保护和返回地址
对以上这些数据,在函数開始调用时分配动态存储空间。函数结束时释放这些空间。
3. 从用户内存空间角度分为三个部分
1. 程序区
一行一行的等待执行的机器码
2. 静态存储区
3. 动态存储区
4. 从C程序执行时
又可分为下面存储区
1. 代码段 (Code | Text)
代码段由程序中执行的机器代码组成。
在C语言中。程序语句进行编译后,形成机器代码。在执行程序的过程中,CPU的程序计数器指向代码段的每一条
机器代码
。并由处理器依次执行。
2. 仅仅读数据段(ROData)
2.1 ROData介绍
- 仅仅读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式相似查表式的操作。因为这些变量不须要更改,因此仅仅须要放置在仅仅读存储器中就可以。
- 仅仅读数据段由程序中所使用的数据产生,该部分数据的特点是在执行中不须要改变,因此编译器会将该数据段放入仅仅读的部分中。C语言中的
仅仅读全局变量
,仅仅读局部变量
。程序中使用的常量
等会在编译时
被放入
到仅仅读数据区
。注意
:定义全局变量const char a[100]={“ABCDEFG”};将生成大小为100个字节的仅仅读数据区,并使用“ABCDEFG”初始化。假设定义为:const char a[ ]={“ABCDEFG”};则依据字符串长度生成8个字节的仅仅读数据段(还有’\0’),
所以在仅仅读数据段中,一般都须要做全然的初始化。
2.2 Example
#define A=18; ##常量
const int A = 18; ##仅仅读全局变量
int main(){
const int B = 18; ##仅仅读局部变量
}
3. 已初始化读写数据段(RW data)
3.1 RWData介绍
- 已初始化数据是在程序中声明,而且具有初值的变量。这些变量须要占用存储器的空间。在程序执行时它们须要位于可读写的内存区域内。并具有初值。以供程序执行时读写。
- 全局变量所有存放在静态存储区,
在程序開始执行时给全局变量分配存储区
。程序行完成就释放
。在程序执行过程中它们占领固定的存储单元
,而不动态地进行分配和释放
;全局变量
静态(static) 局部变量
3.2 Example
int global_init_val=1; ## 全局变量
int main(int argc, char * argv[]){
static int a=1; ## 静态(static) 局部变量
}
4. 未初始化数据段(BSS)
4.1 BSS介绍
未初始化数据是在程序中声明,可是没有初始化的变量,这些变量在程序执行之前不须要占用存储器的空间。
4.1 Example
int global_noinit_val; ## 全局未初始化全局变量
char *p1; ## 全局未初始化全局变量
int main(int argc, char * argv[]){
......
}
5. 堆(heap)
5.1 堆空间介绍
堆内存
仅仅在程序执行时出现
。一般由程序猿分配
和释放
。在具有操作系统的情况下,假设
程序没有释放
,操作系统
可能在程序
(比如一个进程
)结束后回收内存
。
5.2 Example
p1 = (char*) malloc(10); ## 分配得来的10和20个字节的区域就在堆区
p2 = (char*) malloc(20);
6. 栈(stack)
6.1 栈空间介绍
- 栈内存
仅仅在程序执行时出现
。在函数内部使用的变量
、函数的參数
以及返回值
将使用栈
空间,- 栈空间由编译器自己主动分配和释放。
栈空间
是动态开辟
与回收
的。在函数调用过程中,假设函数调用的层次比較多
,所须要的栈空间也逐渐加大
- 对于
參数的传递
和返回值
,假设使用较大的结构体
,在使用的栈空间也会比較大
。
6.2 栈区主要用于下面数据的存储
- 函数内部的动态变量
- 函数的參数
- 函数的返回值
6.3 Example
void main(void){
int b; ## 栈
char s[] = "abc"; ## 栈
char *p2; ## 栈
char *p3 = "123456"; ## 123456\0在常量区 ## p3 在栈上。
}
===================================华丽的切割线=========================
5. 4种局部变量和全局变量的存储类别(static
, extern
, auto
, register
)
5.1 Static
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。
int f(int a)
{
auto int b=0;
static int c=3;
b=b+1;
c=c+1;
return(a+b+c);
}
int main(void)
{
int a=2,i;
for(i=0;i<3;i++)
printf("%d",f(a));
}
对静态局部变量的说明:
1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个执行期间都不释放。而自己主动变量(即动态局部变量)属于动态存储类别。占动态存储空间,函数调用结束后即释放。
2)静态局部变量在编译时赋初值,即仅仅赋初值一次;而对自己主动变量赋初值是在函数调用时进行,每调用一次函数又一次给一次初值。相当于执行一次赋值语句。
3)假设在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自己主动赋初值0(对数值型变量)或空字符(对字符变量)。而对自己主动变量来说,假设不赋初值则它的值是一个不确定的值。
5.2 Extern
外部变量
(即全局变量
)是在函数的外部定义的。它的作用域为从变量定义处開始,到本程序文件的末尾。假设外部变量不在文件的开头定义,其有效的作用范围仅仅限于定义处到文件终了。假设在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern
对该变量作“外部变量声明
”。
表示该变量是一个已经定义的外部变量
。有了此声明。就能够从“声明
”处起。合法地使用该外部变量
。
int max(int x,int y)
{
int z;
z=x>y?x:y;
return(z);
}
int main(void)
{
extern A,B;
printf("%d\n",max(A,B));
}
int A=13,B=-8;
说明:
在本程序文件的最后1行定义了外部变量A。B,但因为外部变量定义的位置在函数main之后,因此本来在main函数中不能引用外部变量A,B。如今我们在main函数中用extern对A和B进行“外部变量声明”,就能够从“声明”处起,合法地使用该外部变量A和B。
5.3 Auto
函数中的局部变量。如不专门声明为static存储类别,都是动态地分配存储空间的。数据存储在动态存储区中
。函数中的形參和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自己主动释放这些存储空间。这类局部变量称为
自己主动变量
。自己主动变量用关键字auto作存储类别的声明。
int f(int a) /*定义f函数,a为參数*/
{
auto int b,c=3; /*定义b,c自己主动变量*/
}
a是形參,b,c是自己主动变量,对c赋初值3。执行完f函数后,自己主动释放a。b,c所占的存储单元。
关键字auto能够省略,
auto不写则隐含定为“自己主动存储类别”,属于动态存储方式
。占用
栈空间
5.4 Register
为了提高效率,C语言同意将局部变量得值放在CPU中的寄存器中,这样的变量叫“寄存器变量”,用关键字register作声明。
int fac(int n)
{
register int i,f=1;
for(i=1;i<=n;i++)
f=f*I;
return(f);
}
int main(void)
{
int i;
for(i=0;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
}
说明:
1)
仅仅有局部自己主动变量和形式參数能够作为寄存器变量
。2)一个计算机系统中的寄存器数目有限,
不能定义随意多个寄存器变量
。3)
局部静态变量不能定义为寄存器变量
。
6. 总结
- 从变量的作用域(即从
空间
)角度来分
,能够分为全局变量
和局部变量
。 - 从变量值存在的作时间(即
生存期
)角度来分
,能够分为静态存储
方式和动态存储
方式。 代码段
、仅仅读数据段
、读写数据段
、未初始化数据段
属于静态区域
静态区域
: 是指在程序执行期间分配固定的存储空间的方式堆
和栈
属于动态区域
动态区域
: 是在程序执行期间依据须要进行动态的分配存储空间的方式。代码段
、仅仅读数据段
和读写数据段
将在链接之后产生
未初始化数据段
将在程序初始化的时候开辟
- 而
堆
和栈
将在程序的执行中分配和释放
。 - C语言程序分为
映像
和执行时
两种状态。在编译-连接后形成的映像中
,将仅仅包括代码段(Text)
、仅仅读数据段(RO Data)
和读写数据段(RW Data)
。 - 在
程序执行之前
。将动态生成未初始化数据段(BSS
) - 在
程序的执行时
还将动态形成堆(Heap)
区域和栈(Stack)
区域。一般来说,在静态的映像文件里,各个部分称之为
节(Section)
,而在执行时的各个部分称之为段(Segment)
。假设不具体区分,能够统称为段
。 - C语言在
编译和连接后
,将生成代码段
(Text)、仅仅读数据段(RO Data
)和读写数据段(RW Data
)。在
执行时
,除了以上三个区域外,还包括未初始化数据段(BSS
)区域和堆(Heap
)区域和栈(Stack
)区域。
7. 一些实例
const char ro[ ] = {"this is read only data"}; //仅仅读数据区
static char rw_1[ ] ={"this is global read write data"}; //已初始化读写数据段
char BSS_1[ 100]; //未初始化数据段
const char *ptrconst ="constant data"; //字符串放在仅仅读取数据段
int main()
{
short b; //在栈上。占用2个字节
char a[100]; //在栈上开辟100个字节, 它的值是其首地址
char s[ ]="abcdefg"; //s在栈上,占用4个字节,"abcdefg"本身放置在仅仅读数据存储区,占8个字节
char *p1; //p1在栈上。占用4个字节
char *p2="123456"; //p2 在栈上。p2指向的内容不能改,“123456”在仅仅读数据区
static char rw_2[ ]={"this is local read write data"};//局部已初始化读写数据段
static char BSS_2[100]; //局部未初始化数据段
static int c = 0; //全局(静态)初始化区
p1=(char *)malloc(10 * sizeof(char ) ); //分配内存区域在堆区
strcpy(p1,"xxxx"); //“XXXX”放在仅仅读数据区。占5个字节
free(p1); //使用free释放p1所指向的内存
return 0;
}
读写数据段包括了忆初始化的全局变量 static char rw_1[ ]以及局部静态变量static rw_2[ ].其区别在于编绎时,是在函数内部使用的还是能够在整个文件里使用。对于rw_1[] 不管有无static 修饰。其都将被放置在读写数据区。仅仅是是否能被其他文件引用与否。
对于后者就不一样了。它是局部静态变量。放置在读写数据区,假设没static修饰,其意义全然改变,它将会是开辟在栈空间的局部变量,而不是静态变量。在这里rw_1[],rw_2[]后没具体数值。表示静态区大小同后面字符串长度决定。
对于未初始化数据区BSS_1[100]与BSS_2[100]。其区别在于前者是全局变量。在所有文件里都能够使用;后者是局部变量,仅仅在函数内部使用。未初始化数据段不设置后面的初始化数值,因此必须使用数值指定区域的大小,编绎器将依据大小设置BSS中须要添加的长度。
參考文章