内存管理,结构体

xiaoxiao2021-02-28  4

1. 数据存放位置

答:

① bss 段用来存放那些没有初始化和初始化为0的全。0局变量

② data 段存放初始化过的全局变量,(非零)

③ rodata 存放常量数据,字符串自动放到rodata中,其他数据需要加const关键字修饰。

④ text 段存放代码(如函数)和部分整数常量,和rodata段相似,不同在于这个段可以执行。

⑤ stack 用于存放临时变量和函数参数。

⑥ heap 是最灵活的一种内存,它的生命周期完全由使用者控制

 l malloc : 用来分配一块指定大小的内存。

 l realloc : 用来调整/重分配一块存在的内存。

 l free: 用来释放不再使用的内存。

 

2. 内存分配方式

答:

存分配方式有三种:

① 从静态存储区域分配。内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在,如全局变量、static变量等

② 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算使用内置于处理器的指令集,效率很高,但分配的内存容量有限。

③ 从堆上分配,亦称动态内存分配。程序在运行时于用mallocnew申请所需要的内存,程序员自己负责在何时用freedelete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

④ 全局变量和static变量是程序整个程序需要用到的,单独分出一块存储区在程序的整个运行期间该存储区存储的数据不清空;

 

3. 野指针

答:

野指针不是NULL指针,是指向“垃圾”内存的指针。野指针的成因有两种:

① 指针变量没有被初始化。

② 指针pfree或者delete之后,没有置为NULL;让人误认为P个合法的指针。

 

4. 指针与数组对比

:

CC+程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变,所以常用指针来操作动态内存。指针远比数组灵活,但也更危险下面以字符串为例比较指针与数组的特性。

 

5. 无参宏定义

答:#define 标识符 字符串

还要说明以下几点

(1) 宏定义是用宏名来表示符串,在宏展开时又以该字符串取代宏名,这只是种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现

(2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换

(3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用“#undef”。

(4)宏名在源程序中若用引号括起来,则预处理程序不对于其进行宏代换

(5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换

(6)对“输出格式”作宏定义,可以减少书写麻烦

 

 

6. 带参宏定义

答:#define 宏名(形参表) 字符串

调用的形式为:宏名(实参表);

带参宏定义与自定义函数的区别如下

(1) 带参宏定义中,形式参数不分配内存单元,因此不必作类型定义;而宏调用中的实参有具体的值,要用它们去代换形参,因此必须作类型说明。这与函数中的情况是不同的在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题

(2) 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式

注意:在宏定义中,字符串内的形参通常要用括号括起来以避免出错

 

7. 条件编译的三种形式

① #ifdef 标识符

程序段1

 #else

程序段2

        #endif

 

 

② #ifndef 标识符

程序段1

 #else

程序段2

        #endif

 

③ #if 常量表达式

程序段1

        #else

 程序段2

#endif

 

 

 

8. #define 和 typedef 的区别

答: 

typedef 只是为了增加可读性而为标识符另起的新名称(仅仅是个别名),而#define原本在C语言中是为了定义常量。到了C++语言, constenum、inline的出现使它也渐渐成为了起别名的工具,有时很容易搞不清楚define与typedef两者到底该用哪个好,如“#define INT int ”这样的语句,用typedef一样可以完成,用哪个好呢?我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的编译器又做了扩充。为了尽可能地兼容,一般都遵循# define 定义“可读”的常量和一些宏语句,而typedef常用来定义关键字、冗长的类型的别名。宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致新命名的标识符具有更易定义变量的功能( int*)PINT以及下面这行define PINT2 int

 

9. 位段结构在内存中的存储方式对于位段结构

答: 编译器会自动进行存储空间的优化,主要的原则为

1)如果位段存储单元能够存储位段结构中的所有成员,那么位段结构中的所有成员只能放在一个位段存储单元中,不能放在两个位段存储单元中;如果个位段存储单元不能容纳位段结构中的所有成员,那么从剩余的位段从下一个位段存储单元开始存放(在VC中位段存储单元的大小是4字节)

(2)如果一个位段结构中只有一个占有0位的无名位段,则只占1或0字节的空间(C语言中是占0字节,而C++中占1字节);否则其他任何情况下,一个位段结构所占的空间至少是一个位段存储单元的大小。

 

10. 常见内存错误及对策

答:

(1) 内存分配未成功,却使用了它。编程新手常犯这种错误,因为他们没有意识到内存配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函的参数,那么在函数的入口处用“assert(p!=NULL)”进行检查;如果是用malloc或new来申请内存,应该用“ifp==NULL)”或“if(p!=NULL)”进行防错处理

(2) 内存分配虽然成功,但是尚未初始化就引用它。犯这种错误主要有两个原因是没有初始化的观念;二是误以为内存的默认初值全为零,导致引用初值错误(如数组)内存的默认初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有,所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

(3) 内存分配成功并且已经初始化,但操作越过了内存的边界。例如,在使用数组时经常发生下标“多1”或者“少1”的操作,特别是在for循环语句中,循环次数很容易搞错,致数组操作越界

(4) 忘记了释放内存,造成内存泄漏。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误,总有一次程序会突然死掉,系统出现内存耗尽的提示。动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则有错误(new/delete同理)

(5) 释放了内存程序继续使用它,有以三种情况

 l 对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放存此时应该重新设计数据结构,从根本上解决对象管理的混乱局面函数

 l 函数return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引1用”,因为内存时被自动销毁。

 l 函数体使用freedelete释放了内存后,没有将指针设置为NULL,导致产生野指针

 

 

转载请注明原文地址: https://www.6miu.com/read-1750239.html

最新回复(0)