C语言—static 的作用、局部变量全局变量、内存结构、堆栈区别

static

在C语言中,static主要定义全局静态变量,定义局部静态变量,定义静态函数

一、定义全局静态变量:在全局变量前面加上关键字static,该全局变量变成了全局静态变量。 全局静态变量有以下特点:
(1)在全局数据区内分配内存
(2)如果没有初始化,其默认值为0
(3)该变量在本文件内从定义开始到文件结束可见(这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。)

二、 定义局部静态变量:在局部静态变量前面加上关键字static,该局部变量便成了静态局部变量。静态局部变量有以下特点:
(1)该变量在全局数据区分配内存
(2)如果不显示初始化,那么将被隐式初始化为0,若初始化,则static修饰的静态局部变量只初始化一次
(3)它始终驻留在全局数据区,直到程序运行结束,延长了局部变量的生命周期,直到程序运行结束以后才释放。
(4)其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。

三、定义静态函数:在函数的返回类型加上static关键字,函数即被定义成静态函数。静态函数有以下特点:
(1)静态函数只能在本源文件中使用
(2)在文件作用域中声明的inline函数默认为static
说明:静态函数只是一个普通的全局函数,只不过受static限制,他只能在文件坐在的编译单位内使用,不能呢个在其他编译单位内使用。


局部变量全局变量

区别:
(1)局部变量只在本函数范围有效,在此函数以外是不能使用这些变量;全局变量的有效范围是从定义变量的位置开始到本源文件结束。
(2)局部变量是程序运行到该函数时给该变量分配内存空间,函数结束则释放该内存空间;全局变量是程序运行时事先分配内存空间,当程序结束时释放内存。

重名问题:
可以重名,局部会屏蔽全局。要用全局变量,需要使用 ::
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。


内存分配

栈区:
在函数中定义的变量存放的内存区域。
常见的int、float、char等变量均存放于栈区中,它的特点是由系统自动分配与释放,不需要程序员考虑资源回收的问题,方便简洁。
ps:栈区的地址分配是从内存的高地址开始向地地址分配。

堆区:
程序员通过指令自主向系统申请的内存区域,大小由程序员决定,它在使用完后同样需要程序员通过指令去释放该区域内存,否则将有可能出现内存的浪费与溢出。
C语言中申请堆区指令为:

int *p = (int *) malloc( N * sizeof(int) );  //分配N个int型(4字节)的内存,即 4 * N 个字节

ps:但指针p存放于栈区。

C语言中释放堆区指令为:

free( p ); //注意此处参数为指针

使用中应该注意,尽量不要去修改p指针对应的地址值,否则在内存释放时将出现错误。(编译可通过,运行出现问题)

全局变量&静态变量区:
全局变量与静态变量本应是两个概念,但由于它们在内存中存放的区域相同,所以将他们放在一起讨论。

全局变量:
位于所有函数外部定义的变量,在整个工程中可见,可修改。

静态变量:
位于所有函数内部定义的由 static 修饰的变量,仅在定义的函数中可见,可修改。(这是它与全局变量的关键区别)
ps:静态变量仅在第一次创建时初始化一次,之后自动跳过初始化语句。
全局变量与静态变量均由系统分配和释放内存,若未对它们进行初始化操作,系统将自动将其值设置为0。(堆区与栈区则不会)

文字常量区:
用于存放文字等不可修改的常量,由系统分配和释放内存。

常见的

char *s = "HelloWorld";//该字符串 HelloWorld 即存放于文字常量区,不可修改但指针s存放于栈区。

pps:若在程序中尝试对其修改(例如尝试修改第一个字符 *s = ‘h’;),将出现编译可通过,运行报错的情况。
同时因注意它与const修饰的变量之间的区别:

char aa = 'A';//aa存放于栈区
const char bb = 'B'; //bb同样存放于栈区

const修饰的变量仅仅用于告诉编译器bb是一个常量,如果后续的程序中有出现尝试修改bb的操作时,编译将报错。
这种写法主要是为了防止程序员在后续的代码中误操作bb变量而添加的一个约束条件,并不会影响它存放的位置。

程序代码区:
用于存储程序编译连接后生成的二进制机器码指令的内存区域。该部分内容可通过反汇编操作将机器码转换为汇编语言。


堆栈区别

(1)申请方式不同:栈(英文名称是stack)是系统自动分配空间的,例如我们定义一个char a;系统会自动在栈上为其开辟空间。而堆(英文名称是heap)则是程序员根据需要自己申请的空间,例如malloc(10);开辟十个字节的空间。由于栈上的空间是自动分配自动回收的,所以栈上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。而堆上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露。

(2)申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的 delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 也就是说堆会在申请后还要做一些后续的工作这就会引出申请效率的问题。

(3)申请效率的比较
栈:由系统自动分配,速度较快。但程序员是无法控制的。
堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

(4)申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

(5)堆和栈中的存储内容
由于栈的大小有限,所以用子函数还是有物理意义的,而不仅仅是逻辑意义。
栈: 在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

(6)存取效率的比较
char s1[] = “aaaaaaaaaaaaaaa”;
char *s2 = “bbbbbbbbbbbbbbbbb”;
aaaaaaaaaaa是在运行时刻赋值的;放在栈中。
而bbbbbbbbbbb是在编译时就确定的;放在堆中。
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

你可能感兴趣的:(c语言)