内存管理,结构体

1、程序的内存分配,即一个由C/C++编译的程序占用的内存分为几个部分?

(1)栈区(stack)——由编译器自动分配释放,在不需要的时候自动清除。用于存放函数的参数、局部变量等。操作方式类似数据结构中的栈(后进先出)。


(2)堆区(heap)——一般由程序员分配释放,若程序员分配后不释放,程序结束后可能由OS回收。不同于数据结构中的堆,分配方式有些类似链表。


(3)全局区(静态区)——全局变量和静态变量存储在这里。程序结束后由系统释放。在以前到C语言中,全局变量又细分为初始化的(DATA段)和未初始化到(BSS段),在C++里已经没有这个区分了,它们共同占用同一块内存区。


(4)常量存储区——常量字符串就存放在这里。一般不允许修改。程序结束后由系统释放。


(5)代码区——存放函数体的二进制代码。


2、内存的分配方式都有什么?

[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
[3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏。


3、数组和指针的区别?

1、基本概念:
 数组:一个数组名就代表一个数组对象,这个对象内可以有一个或多个元素,每个元素类型都相同;
 指针:而指针里一般存的都是地址值。
2、赋值与访问:
数组:对于数组来说,它的内存地址在编译的时候就已经确定了,可以直接给它赋值,当然也可以直接对它进行访问。
指针:对于指针来说,先得得到之指针里存放的地址值,然后向这个地址赋值;
3、所占内存:
数组:和它所存数据的元素个数和类型有关系
指针:和操作系统的位数有关
4、联系:
对数组进行取地址操作时候,返回的类型是一个指向数组首地址的指针;
数组作为右值的时候,就相当于指针,返回的是第一个元素的值;
对数组进行取下标操作时,也类似于指针;


4、堆和栈的区别?

(1)空间分配

栈(操作系统):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈,栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些

(2)数据结构
堆(数据结构):堆可以被看成是一棵树,如:堆排序
栈(数据结构):一种后进先出的数据结构

(3)缓存方式

栈使用的是一级缓存,他们通常是被调用时处于系统空间中农,调用完毕立即释放。

堆是存放于一级缓存生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。多以调用这些对象的速度相对来说要低一些。


5、将一个结构体变量的值传递给另一个函数的三种方法

(1)用结构体变量名作参数。
(2)用指向结构体变量的指针作实参,将结构体变量的地址传给形参。
(3)用结构体变量的引用变量作函数参数。


6、#define和typedef的区别

(1) 首先,二者执行时间不同
关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。
Define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

(2) 功能不同
Typedef用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。

#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

(3)作用域不同
#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef有自己的作用域。

(4)对指针的操作
二者修饰指针类型时,作用不同。


7、结构体和联合体的区别

(1)在任何同一时刻,union中只存放了一个被选中的成员,而struct的所有成员都存在。

(2)在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。

(3)对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。


8、什么叫作内存泄漏?
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
简单的说,内存泄漏分为两方面:1、非法访问空间 2、访问了已经释放的空间。


9、如何避免野指针?
(1)首先我们要养成良好的编码习惯。
(2)当我们定义一个指针的时候,直接让它指向NULL。因为NULL在宏定义是#define NULL (void *) 0  它代表的是零地址,而零地址是不能进行读写操作的。
(3)要想给指针指向的空间赋初值的时候,必须给指针分配空间。
可是使用malloc()进行分配空间,它返回的是空间的首地址。
(4)在分配空间后,我们首要做的是检查是否分配成功。
简单的检测方法:
if (p == NULL)     //p为指针变量
{
printf(“malloc分配失败!”);
eixt(1);      //结束整个程序,return 是结束一个函数
}
(5)在使用malloc分配后的空间之前一定要对其清零。
因为我们分配的空间里面可能残留一些垃圾数据,这样会导致程序出错。
(6)使用完空间后,记得释放空间,可以用free(p);//p为指针变量
(7)释放完后可以让指针指向NULL,如:p = NULL


C语言常见内存错误及对策
(1)内存未分配成功,却使用了它。
方   法:在使用之前检查指针是否为NULL。
             1)当指针p是函数的参数时,在函数入口处用语句assert(p!=NULL)进行断言检查。
             2)当使用malloc或new来申请内存时,应该用if(p != NULL)进行防错检查。
(2)引用了尚未初始化的指针
原   因:内存的缺省初始值究竟是什么并没有统一的标准,在使用之前都进行初始化。
              1)没有初始化的观念。
              2)内存的缺省值是未定义,即垃圾值。
(3)越界操作内存
原   因:内存分配成功且初始了,但越界操作是不允许的。
例   如:在使用数组时经常发生下标“多1”或“少1”,特别是在for循环语句时。
(4)忘记释放内存,造成内存泄漏。
原   因:含有这种类型错误的函数,每被调用一次,就丢失一块内存。当内存充足时看不到这种错误带来的影响,当内存耗尽时系统提示:“内存耗尽”。因此,动态内存的申请与释放必须配对,程序中malloc与free的使用次数要相同。
(5)释放了内存却继续使用它
原   因:对应的情况有2种
              1)返回了“栈内存的指针或引用”,因为堆栈中的变量在函数结束后自动销毁。
              2)某块内存被free后,没有将指向该内存的指针设置为NULL,导致产生“野指针”。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值