C++语言基础五

1、堆和栈区别

由编译器进行管理,在需要时由编译器自动分配空间,在不需要时候自动回收空间,一般保存的是局部变量和函数参数等。

连续的内存空间,在函数调用的时候,首先入栈的是主函数的下一条可执行指令的地址,然后是函数的各个参数。

大多数编译器中,参数是从右向左入栈(原因在于采用这种顺序,是为了让程序员在使用C/C++的“函数参数长度可变”这个特性时更方便。如果是从左向右压栈,第一个参数(即描述可变参数表各变量类型的那个参数)将被放在栈底,由于可变参数的函数第一步就需要解析可变参数表的各参数类型,即第一步就需要得到上述参数,因此,将它放在栈底是很不方便的。)本次函数调用结束时,局部变量先出栈,然后是参数,最后是栈顶指针最开始存放的地址,程序由该点继续运行,不会产生碎片。

栈是高地址向地址拓展,栈低高地址,空间较小。

由程序员管理,需要手动new malloc delete free进行分配和回收,如果不进行回收的话,会造成内存泄露的问题。

不连续的空间,实际上系统中有一个空闲链表,当有程序申请的时候,系统遍历空闲链表找到第一个大于等于申请大小的空间分配给程序,一般在分配程序的时候,也会在空间头部写入内存大小,方便delete回收空间大小。当然如果有剩余的,也会将剩余的插入到空闲链表中,这也是产生内存碎片的原因。

堆是由低地址向高地址拓展,空间较大,较为灵活。

2、函数传递参数的几种方式

值传递:形参是实参的拷贝,函数内部对形参的操作并不会影响到外部的实参。

指针传递:也是值传递的一种方式,形参是指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行操作。

引用传递:实际上就是把引用对象的地址放在了开辟的栈空间中,函数内部对形参的任何操作可以直接映射到外部的实参上面。

3、new / delete,malloc / free区别

都可以用来在堆上面分配和回收空间。new / delete是操作符,malloc / free是库函数。

执行new实际上执行两个过程:1、分配未初始化的内存空间(malloc);2、使用对象的构造函数对空间进行初始化;返回空间的首地址。如果在第一步分配空间中出现问题,则抛出std::bad_alloc异常,或被某个设定的异常处理函数捕获处理;如果在第二步构造对象时出现异常,则自动调用delete释放内存。

执行delete实际上也有两个过程:1、使用析构函数对对象进行析构;2、回收内存空间(free)。

以上也可以看出new和malloc的区别,new得到的是经过初始化的空间,而malloc得到的是未初始化的空间。所以new是new一个类型,而malloc则是malloc一个字节长度的空间。delete和free同理,delete不仅释放空间还析构对象,delete一个类型,free一个字节长度的空间。

为什么有了malloc / free还需要new / delete?因为对于非内部数据类型而言,光用malloc / free无法满足动态对象的要求。对象在创建的同时需要自动执行构造函数,对象在消亡以前要自动执行析构函数。由于malloc / free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc / free,所以有了new / delete操作符。

4、volatile和extern关键字

volatile三个特性

易变性:在汇编层面反映出来,就是两条语句,下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是重新从内存中读取。

不可优化性:volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。

顺序性:能够保证volatile变量之间的顺序性,编译器不会进行乱序优化。

extern

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。

注意extern声明的位置对其作用域也有关系,如果是在main函数中声明的,则只能在main函数中调用,在其他函数中不能调用。其实要调用其它文件中的函数和变量,只需把该文件用#include包含进来即可,为啥要用extern?因为用extern会加速程序的编译过程,这样能节省时间。

在C++中extern还有另外一种作用,用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern“C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同,用此来解决名字匹配的问题。

5、define和const区别(编译阶段、安全性、内存占用等)

对于define来说,宏定义实际上是在预编译阶段进行处理,没有类型,也就没有类型检查,仅仅做的是遇到宏定义进行字符串的展开,遇到多少次就展开多少次,而且这个简单的展开过程中,很容易出现边界效应,达不到预期效果。因为define宏定义仅仅是展开,因此运行时系统并不为宏定义分配内存,但是从汇编的角度来讲,define却以立即数的方式保留了多份数据的拷贝。

对于const来说,const是在编译期间进行处理的,const有类型,也有类型检查,程序运行时系统会为const常量分配内存,而且从汇编的角度讲,const常量在出现的地方保留的是真正数据的内存地址,只保留了一份数据的拷贝,省去了不必要的内存空间。而且,有时编译器不会为普通的const常量分配内存,而是直接将const常量添加到符号表中,省去了读取和写入内存的操作,效率更高。

6、计算下面几个类的大小

class A{}; // 空类在实例化时得到一个独一无二的地址,所以为1.
sizeof(A) = 1; // 当C++类中有虚函数的时候,会有一个指向虚函数的指针(vptr)
class A{virtual Fun(){} }; 
sizeof(A) = 4(32bit) / 8(64bit);
class A{static int a;};
sizeof(A) = 1;
class A {int a;}; 
sizeof(A) = 4;
class A{static int a; int b;};
sizeof(A) = 4;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值