内存管理
1.作用域
(1)文件作用域:全局变量,定义在函数外面的变量
int a=10;
void main(){
int a = 20; // 函数内部可以再次定义和全局变量同名的变量,定以后a为20,没有定义为10
printf("%d\n", a);
}
(2)extern关键字
c #include "c.h" // 在这个文件中定义了age=10 extern int age ; // 声明该变量在其他文件中定义了,连接时使用就可以了。这文件需要是.c文件,不能是.h文件。gcc编译时后面加上两个文件名 setage(int n){ age = n; } getage(){ printf("%d\n", age); } void main(){ setage(11); getage(); // 11 }
(3)auto自动变量:不写auto,就默认为auto变量
(4)register 变量:建议变量放到空闲的寄存器中:寄存器变量不能取地址操作
(5)static 变量:只初始化一次,程序运行期间一直存在 。static只是增大了变量的存在时间,却没增大变量的作用域
一旦全局变量被static修饰,则这个变量的作用域被限制在该文件内部,其他文件不能用extern使用
c void mystatic(){ static int a = 0; // 不加static,循环打印每次打印都是0,加上static每次打印+1 printf("%d\n", a); a++; } void main(){ for (int i = 0; i < 10; ++i) { mystatic(); } }
2.C语言中,函数都是全局的。
(1)如果函数前面加了static,则函数被限制在文件内部使用
(2)函数前面加不加extern一样。而变量前面的extern是可以省略的,比如:int a; 如果其他文件中定义了int a=10,则此处的变量是声明,如果其他文件中没有定义a,则此处是定义
(此处也是说明c语言并不严谨)
3.c语言内存管理
程序在内存中分为4个区域:
(1)栈:
不会很大,栈空间是操作系统为每个程序固定分配的大小以k为单位。栈空间是操作系统为每个程序固定分配的大。此部分的内存表现为先进后出。所有自动变量,函数行参都有编译器自动放到栈中。当一个自动变量超出其作用域时,自动从栈中弹出。先进入栈中的变量放到大内存号中
每个线程都有自己的栈
int main(){
char array[1024*1024*1024] = {0}; //定义一个超大的数组就会栈溢出
return 0;
}
(2)堆:
堆没有大小限制,只是无力限制。但是堆内存不能由编译器自动释放
#include <stdlib.h>
int main(){
int p = malloc(sizeof(int)10); // 在堆中申请了10个int的大小
char p2 = malloc(sizeof(char)10);
memset(p,0,sizeof(int)*10); // malloc申请的堆内存 ,在代码的操作上相当于数组
for(int i=0;i<10;i++){
p[i] = i; // 操纵数组
}
free(p) ; // 释放通过malloc申请的堆内存
free(p2);
}
(3)静态区:
所有的全局变量,以及程序中的静态变量都存储在静态区 (4)代码区:程序在被操作系统加载到内存中时,所有可执行代码加载到代码段,这块内存不能再程序运行期间修改c
int c= 0;
void main(){
int a=1;
int b=2;
static int d = 3;
printf("%d,%d,%d,%d,%d\n", &a,&b,&c,&d,main); // 1321929276,1321929272,6295612,6295604,4195632
}
上述程序的a和b在栈区,所以地址号紧挨着:1321929276,1321929272 发现a所在的内存号在高位。
c和d在静态区:地址号紧挨着
main在代码段
4.堆栈的一些问题
(1)不能将一个栈变量的地址作为函数的返回值,因为函数结束后,栈变量被释放
c eg: int *geta(){ int a = 10; return &a; }
(2)但是可以把堆变量的地址作为函数返回值返回
int geta(){
int p = malloc(sizeof(int));
return p;
}
int main(){
int p = geta();
malloc(p);
}
(3)不能用变量声明一个数组长度,eg:int arr[i]
, 如果申请内存的大小靠程序运行期间决定,那么就要使用 malloc(sizeof(int)*i)
,要记得free
(4)代码区和静态区的大小都是固定的,静态区是编译器根据代码提前申请的。堆和栈所占大小是动态,但是栈有一个操作系统规定的栈上线shell
cd /proc # 每个进程号都是一个文件
cd pid
cat maps # 查看内存映射,这里列出了代码段,堆,栈,静态区的使用情况(栈的预设范围)
(5)堆的分配 操作系统分配内存的单位不是字节,而是页。cat smps之后,发现堆有个项是内存页大小:4k。页太大,浪费的内存空间太大,但操作系统维护起来很简单,因为不用频繁的申请释放内存
</br>
(6)malloc申请的堆内存上会存在旧数据,所以申请后需要用memset清空 :memset(p,p,sizeof(int)*10)
这两句话的简写就是calloc,这样内存上全部是0
int main(){
char p = calloc(10,sizeof(char)); // 申请大小是10sizeof(char)
}
(7)relloc用于扩展已经申请的堆内存,在原有内存上,增加连续的内存,如果没有连续空间可扩展,则会新分配一个连续空间,将原有内存拷贝到新空间,然后释放原有内存。但是relloc只申请,不打扫(内存中有旧数据)
// rello(p,新的内存大小),.如果p为NULL,则realloc等于malloc,不指定分配空间的起始位置
void main(){
char p1 = calloc(10,sizeof(char));
char *p2 = realloc(p1,5); // p1指向的地址变为5
// realloc(NULL,5) = malloc(5)
}