0
点赞
收藏
分享

微信扫一扫

C++校招面经(三)

鱼板番茄 2024-09-22 阅读 28

欢迎关注 0voice · GitHub

11 、指针和引⽤的区别

指针和引⽤都是⼀种内存地址的概念,区别呢,指针是⼀个实体,引⽤只是⼀个别名。

在程序编译的时候,将指针和引⽤添加到符号表中。

指针它指向⼀块内存,指针的内容是所指向的内存的地址,在编译的时候,则是将 指针变ᰁ名 - 指针变ᰁ的地址

加到符号表中,所以说,指针包含的内容是可以改变的,允许拷⻉和赋值,有 const 和⾮ const 区别,甚⾄可以为

空, sizeof 指针得到的是指针类型的⼤⼩。

⽽对于引⽤来说,它只是⼀块内存的别名,在添加到符号表的时候,是将 " 引⽤变ᰁ名 - 引⽤对象的地址 " 添加到符号

表中,符号表⼀经完成不能改变,所以引⽤必须⽽且只能在定义时被绑定到⼀块内存上,后续不能更改,也不能为

空,也没有 const 和⾮ const 区别。

sizeof 引⽤得到代表对象的⼤⼩。⽽ sizeof 指针得到的是指针本身的⼤⼩。另外在参数传递中,指针需要被解引⽤

后才可以对对象进⾏操作,⽽直接对引⽤进⾏的修改会直接作⽤到引⽤对象上。

作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引⽤的实质是传地址,传递的是变ᰁ的地址

12 、 ᰀ (wild) 指针与悬空 (dangling) 指针有什么区别?如何避免?

ᰀ指针 (wild pointer) :就是没有被初始化过的指针。⽤ gcc -Wall 编译 , 会出现 used uninitialized 警告。

悬空指针:是指针最初指向的内存已经被释放了的⼀种指针。

⽆论是ᰀ指针还是悬空指针,都是 指向⽆效内存区域 ( 这⾥的⽆效指的是 " 不安全不可控 ") 的指针 。 访问 " 不安全可

"(invalid) 的内存区域将导致 "Undefined Behavior"

如何避免使⽤ᰀ指针? 在平时的编码中,养成在定义指针后且在使⽤之前完成 初始化 的习惯或者使⽤智能指针。

13 、说⼀下 const 修饰指针如何区分?

下⾯都是合法的声明,但是含义⼤不同:

const int * p1; // 指向 整形常ᰁ 的指针,它指向的值不能修改

int * const p2; // 指向整形的 常ᰁ指针 ,它不能在指向别的变ᰁ,但指向(变ᰁ)的值可以修改。

const int *const p3; // 指向 整形常ᰁ 常ᰁ指针 。它既不能再指向别的常ᰁ,指向的值也不能修改。

理解这些声明的技巧在于,查看关键字 const 右边来确定什么被声明为常ᰁ ,如果该关键字的右边是类型,则值是

常ᰁ;如果关键字的右边是指针变ᰁ,则指针本身是常ᰁ。

14 、简单说⼀下函数指针

从定义和⽤途两⽅⾯来说⼀下⾃⼰的理解:

⾸先是定义:函数指针是指向函数的指针变ᰁ。函数指针本身⾸先是⼀个指针变ᰁ,该指针变ᰁ指向⼀个具体的函

数。这正如⽤指针变ᰁ可指向整型变ᰁ、字符型、数组⼀样,这⾥是指向函数。

在编译时,每⼀个函数都有⼀个⼊⼝地址,该⼊⼝地址就是函数指针所指向的地址。有了指向函数的指针变ᰁ后,

可⽤该指针变ᰁ调⽤函数,就如同⽤指针变ᰁ可引⽤其他类型变ᰁ⼀样,在这些概念上是⼤体⼀致的。

其次是⽤途:调⽤函数和做函数的参数,⽐如回调函数。

示例:

15 、堆和栈区别

由编译器进⾏管理,在需要时由编译器⾃动分配空间,在不需要时候⾃动回收空间,⼀般保存的是局部变ᰁ和函数

参数等。

连续的内存空间,在函数调⽤的时候,⾸先⼊栈的主函数的下⼀条可执⾏指令的地址,然后是函数的各个参数。

⼤多数编译器中,参数是从右向左⼊栈(原因在于采⽤这种顺序,是为了让程序员在使⽤ C/C++ 函数参数⻓度可

这个特性时更⽅便。如果是从左向右压栈,第⼀个参数(即描述可变参数表各变ᰁ类型的那个参数)将被放在

栈底,由于可变参的函数第⼀步就需要解析可变参数表的各参数类型,即第⼀步就需要得到上述参数,因此,将它

放在栈底是很不⽅便的。)本次函数调⽤结束时,局部变ᰁ先出栈,然后是参数,最后是栈顶指针最开始存放的地

址,程序由该点继续运⾏,不会产⽣碎⽚。

栈是⾼地址向低地址扩展,栈低⾼地址,空间较⼩。

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

题。

不连续的空间,实际上系统中有⼀个空闲链表,当有程序申请的时候,系统遍历空闲链表找到第⼀个⼤于等于申请

⼤⼩的空间分配给程序,⼀般在分配程序的时候,也会空间头部写⼊内存⼤⼩,⽅便 delete 回收空间⼤⼩。当然

如果有剩余的,也会将剩余的插⼊到空闲链表中,这也是产⽣内存碎⽚的原因。

堆是低地址向⾼地址扩展,空间交⼤,较为灵活。

16 、函数传递参数的⼏种⽅式

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

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

⾏操作。

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

的实参上⾯。

17 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 ⽆法满⾜动

态对象的要求。对象在创建的同时需要⾃动执⾏构造函数,对象在消亡以前要⾃动执⾏析构函数。由于 mallo

free 是库函数⽽不是运算符,不在编译器控制权限之内,不能够把执⾏的构造函数和析构函数的任务强加于

malloc free ,所以有了 new delete 操作符。

举报

相关推荐

0 条评论