1、分配方式
静态分配:
静态区(全局区),分配全局变量、静态变量优先于main函数。
动态分配:
(1)自动分配:栈区
(2)手动分配:堆区,malloc
,free
2、内存的4种分区
一个由C编译的程序占用的内存大致分为以下几部分:
栈区(stack):
动态分配。由编译器自动分配释放,存放函数的参数值,局部变量的值等。栈顶从高地址往低地址
方向增长。最多1MB。
堆区(heap):
动态分配。一般由程序员分配和释放(动态内存申请malloc与释放free),需要手动回收,否则会一直存在,若程序员不释放,程序结束时可能由操作系统回收。堆从低地址到高地址
增长。需要大空间或不定空间时用堆。
全局区(静态区)(static):
静态分配。全局变量和静态变量的存储是放在一块的,初始化的全局变量
和静态变量
在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,该区域在程序结束后由操作系统释放。
程序代码区:
静态分配。存放函数体的二进制代码,字符串常量和其他常量(如#define X 10)的存储位置,程序结束后由操作系统释放。只可读不可写。
3、变量的形式
全局变量:extern
全局变量能被另一个c文件调用,加不加extern都行,只需要重新声明下即可。
全局变量定义的基本格式为:
extern 类型变量名=初始化表达式
;此时,初始化表达式不可省略,此指令通知编译器在静态存储区中开辟一块指定类型大小的内存区域,用于存储该变量。C语言规定,只要是在外部,即不是在任何一个函数内定义的变量,编译器就将其当作全局变量,无论变量定义前是否有extern说明符。也就是说,只要在函数外部书写下述形式即可int m=100;当全局变量定义时,
当且仅当
省略了extern时,初始化表达式才可省略,系统默认将其初始化为0
,对于定义的全局数组或结构,编译器将其中的每个元素或成员的所有位都初始化为0
。
全局变量的好处:
(1)为函数间数据传递提供了新的途径,函数返回值仅仅只能有1个,很多情况下,这不能满足要求,而全局变量可用于更多处理结果。
(2)利用全局变量可以减少形参和实参的个数,省去函数调用时的时空开销,提高程序运行的效率。
全局变量的缺点:
(1)全局变量在程序执行期间都有效,一直占据着存储单元,不像局部变量等在调用执行期间临时占用内存,退出函数时便将其释放。最大的问题是降低了函数的封装性和通用性,由于函数中存在全局变量,因此,如果想把函数复用在其他文件中,必须连所涉及的全局变量一块移植过去,容易引发各种问题,造成程序不可靠。全局变量使得函数间独立性下降,耦合度上升,可移植性和可靠性变差。
(2)为了代码的可移植,在可以不使用全局变量的情况下应尽量避免使用全局变量。
#include "pch.h"
#include <iostream>
using namespace std;
int i; //下面程序能正常执行,输出i :0。
int main()
{
cout << "i:" << i << endl;
return 0;
}
#include "pch.h"
#include <iostream>
using namespace std;
extern int i; //程序出错,只声明未定义。
int main()
{
cout << "i:" << i << endl;
return 0;
}
静态变量:static
static变量有静态全局变量和静态局部变量之分:
(1)对局部变量用static声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在。除了生存期是整个程序执行期间(与程序共存亡)外,其作用域与可见域与普通auto变量完全一样。static局部变量只有定义,没有声明。
(2)静态全局变量和extern变量的不同体现在作用域上,extern作用域是本程序的所有源代码文件,只要在一个文件中定义,在其他文件中使用时只要对其进行声明即可。而静态全局变量只有定义没有声明,其作用域仅限于从定义位置起到本文件结束的一段代码区域,不能被其他文件中的函数所使用。
静态全局变量实际上是对extern变量破坏封装性和可靠性的一种改良。
当省略初始化表达式时,编译器自动以0初始化静态变量。对于数组或结构,编译器将其中的每个元素或成员的所有二进制位都初始化为0。
如果要对extern和static变量进行赋值初始化,只能使用常量表达式来初始化extern变量和static变量,常量表达式包括直接常量
、#define 常量
、枚举常量
和sizeof()
运算符,下面的初始化代码都是合法的:
int num;/*编译器自动将num初始化为0*/
int numA=sizeof(double);/*sizeof运算符*
const 常量不行,因为C语言中的const常量是伪常量,仅仅是编译器不能修改。
自动变量:auto
一般情况下,函数的块语句内部或者函数参数,编译器都会自动加上auto,成为一个自动变量。其实就是局部变量(包括结构体、指针等等)定义会加上auto。 初始值一般是随机的。
寄存器变量:register
在程序运行时,根据需要到内存中相应的存储单元中调用,如果一个变量在程序中频繁使用,例如循环变量,那么,系统就必须多次访问内存中的该单元,影响程序的执行效率。
因此,C/C++语言还定义了一种变量,不是保存在内存上,而是直接存储在CPU中的寄存器中,这种变量称为寄存器变量。
寄存器变量的定义形式是:
register 类型标识 符变量名
寄存器是与机器硬件密切相关的,不同类型的计算机,寄存器的数目是不一样的,通常为2到3个,对于在一个函数中说明的多于2到3个的奇存器变量,C编译程序会自动地将寄存器变量变为自动变量。
寄存器说明符只能用于说明函数中的变量和函数中的形参,因此不允许将全局变量
或静态变量
声明为register
。
VC会自动进行寄存器变量优化。频繁使用的变量,会自动优化成寄存器变量。
GCC需要手动指定。
C语言寄存器变量不可以取地址
,C++可以,因为C++会在内存给C语言寄存器保留副本
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
for (register int i = 0; i < 10; i++)
{
cout << "i:" << i << endl;
}
return 0;
}
打开VS中的调试->窗口->寄存器,可以看到CPU中的EAX寄存器不断加1
4、函数种类
静态函数:
C语言默认函数可以跨文件调用,函数的作用域(理论最大范围,区别于可见域)为整个项目,但仍遵守C语言从上到下编译的规则。
static避免函数跨文件重名的问题
static避免本个源文件的函数被外部所用
static加上以后,就叫内部函数,也叫静态函数
全局函数:
C语言默认函数可以跨文件调用。但对于外部函数,根据软件工程规范,最好加上extern进行声明,虽然不加extern进行声明也能正常编译。
网友评论