变量、编译和内存分配
文章目录
1 变量
1.1 变量声明和变量定义的区别
变量定义(definition)为变量分配存储空间,可以指定变量初始值。变量声明(declaration)是指向程序表明变量的类型和名字。定义也是声明,可以用extern关键字声明变量。即把建立存储空间的声明称为定义,把不建立存储空间的声明称为声明。
1.2 全局变量和静态变量的异同
相同:都在静态存储区分配空间,生命周期与程序生命周期相同。
区别:全局变量的作用域是整个程序,静态变量只在定义其的源文件内有效。
1.3 C语言中各种变量的默认初始值
全局变量在定义时若没有初始化,系统将自动为其初始化,数值型为0,字符型为NULL(0),指针变量为NULL;静态变量的情况与全局变量类似;非静态局部变量若没有初始化,则其内容是不可预料的,是随机数,例如指针若未初始化,其值不为NULL,是野指针,对系统安全造成很大的隐患。
2 编译
2.1 编译的过程
预处理:预处理相当于根据预处理命令组装成新的C程序,
编译:将得到的i文件翻译成汇编代码.s文件。
汇编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。该文件是二进制文件。
链接:将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件。
2.2 编译和链接的区别
3 内存分配
3.1 内存分配的方式
从静态存储区分配:在程序编译时进行分配,在程序运行期间一直存在。
在栈上创建:执行函数时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动被释放。栈中的变量初始值是随机的,因此要进行初始化。栈是向下增长的。
从堆上分配:也称动态内存分配,调用malloc或new申请内存,在使用结束时调用free或delete释放内存,生存期有程序员决定。堆是向上增长的。
#include <stdlib.h>
#include <string.h>
int global = 0; //静态存储区
int main()
{
int a = 0; //栈
char *p == NULL; //栈
static int b = 0; //静态存储区
p = (char *)malloc(10); //申请的内存区域在堆中
strcpy(p, "123456789"); //"123456789"在常量区中
return 0;
}
3.2 堆和栈的区别
空间分配:栈由操作系统自动分配释放,堆一般由程序员分配释放。
缓存方式:栈使用的是一级缓存,堆则是存放在二级缓存中。
数据结构:栈是一种先进后出的数据结构,堆可看作一棵树。
3.3 内存泄漏
内存泄露(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。一般是指堆内存的泄露,并非指内存在物理上的消失,而是失去了对该段内存的控制,因而造成了内存的浪费。内存泄露往往会导致系统出现CPU资源耗尽的严重后果。
3.4 缓冲区溢出
缓冲区是程序运行的时候机器内存中的一个连续块,它保存了给定类型的数据。缓冲区溢出是指当向缓冲区内填充数据位数超过了缓冲区自身的容量限制时,发生的溢出的数据覆盖在合法数据(数据、下一条指令的指针、函数返回地址等)上的情况。
缓冲区溢出可能出现两种结果:
- 过长的数据覆盖了相邻的存储单元,引起程序运行失败,严重的可导致系统崩溃。
- 利用这种漏洞可以执行任意指令,甚至可以取得系统root特级权限,进而危害系统安全。
3.5 申请动态内存失败的处理方法
- 判断指针是否为 NULL,如果是则马上用 return 语句终止本函数。
- 判断指针是否为 NULL,如果是则马上用 exit(1) 终止整个程序的运行。
- 为 new 和 malloc 设置异常处理函数。
3.6 思考题
void GetMem(char *p){
p = (char *)malloc(10);
}
char *GetMem(void){
char p[] = "hello world";
return p;
}
void GetMem(char **p){
*p = (char *)malloc(10);
}
void Test(void){
char *str = NULL;
GetMem(str); //程序崩溃,str的值并未改变
str = GetMem(); //可能是乱码,返回的是指向栈内存的指针
GetMem(&str); //正确
free(str);
if(str != NULL) //free(str)之后没有置NULL,str为野指针
{
...
}
}
; //正确
free(str);
if(str != NULL) //free(str)之后没有置NULL,str为野指针
{
…
}
}