CPP基础知识

本文介绍了C++中的基本概念,包括const关键字与宏定义的区别、malloc与free的内存管理机制、new/delete与malloc/free的联系与区别,以及内存泄漏、内存溢出、内存踩踏和野指针的定义。此外,还探讨了引用与指针的异同,函数重载的原理,以及inline函数与宏定义的对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CPP基础知识

1、const关键字与宏定义的区别是什么?

  1. 编译器的处理方式不同。宏定义是在预处理阶段做字符串的替换,而const是在编译时。
  2. 类型和安全检查不同。宏定义没有类型,不做任何类型检查;const常量有自己的类型,编译时作类型检查。实际使用中,应用const替换宏定义,减少犯错误概率。

2、malloc的底层实现是怎样的?free是怎么回收内存的?

  1. malloc函数的实质是它有一个将可用的内存块连接为一个长长的列表的空闲链表。
  2. 调用malloc函数时,它沿着链表找到一个大到足以满足用户请求的内存块,将其一分为二,一块分给用户,一块留在链表中。
  3. 调用free函数时,将用户释放的内存块连接到链表上。
  4. 最后,空闲链表会被切成很多小的内存块,如果这时用户申请一个大内存块,若空闲链表上没有满足要求的内存块,malloc函数会请求延时,并在空闲链表上检查各内存片段,将相邻的小空闲块合并成较大的内存块。

3、new/delete与malloc/free的区别与联系是什么?(面试常考)

​ 联系:1、都是申请堆空间
​ 2、必须成对出现,否则会造成内存泄漏
​ 区别:1、new/delete都是c++的运算符,malloc/free是c语言库函数
​ 2、new在开辟空间的同时可以进行初始化,malloc只会申请空间

4、区分以下概念:内存泄漏、内存溢出、内存踩踏、野指针?(面试常考)

  1. 内存泄漏:是指我们在堆中申请(new/malloc)了一块内存,但是没有去手动的释放(delete/free)内存,导致指针已经消失,而指针指向的东西还在,已经不能控制这块内存。

  2. 内存溢出 out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

  3. 内存踩踏:访问了不合法的地址 。通俗一点就是访问了不属于自己的地址。如果这块地址分配给了另一个变量使用,就会破坏别人的数据。从而导致程序运行异常,挂死,输出图像破图等。

  4. 野指针:指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。

    出现的三种方式:

    1. 指针变量未初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
    2. 指针指向内存空间释放后之后未置空。有时指针在free或delete后未赋值 NULL,便会使人以为是合法的。别看free和delete的名字(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。
    3. 野指针指针操作超越变量作用域。不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

5、引用与指针的区别是什么?并且将"引用"作为函数参数有哪些特点?在什么时候需要使用"常引用"?

Q1:

相同点:都是地址的概念。指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
不同点:1、指针是一个实体,而引用仅是个别名。
2、引用使用时无需解引用( * ),指针需要。
3、引用只能在定义时被初始化一次,之后不可变;指针可变。
4、引用没有const,指针有,const在 * 右边(顶层const)是指针常量,左边(底层const)是常量指针。常量引用是对常量的引用,是底层const。常量引用 const T& 可以引用字面值常量及表达式(右值),且常量引用的不同与T类型对象时,如果可以类型转换则会类型转换为T类型常量引用,不过会产生临时变量。

​ 5、引用不可为空,指针可。
​ 6、sizeof作用与引用得到所指变量(对象)的大小,而作用于指针得到指针本身的大小。
​ 7、指针和引用的自增(++)运算意义不一样。

联系:引用的底层实现是指针常量。引入引用的目的是“用适当的工具做恰如其分的工作”。指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。

Q2:

1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参 变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用 “*指针变量名” 的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

Q3:
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

6、如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

  1. 如果是要你的代码在编译时发现编译器类型,就判断 _cplusplus 或 _ STDC _ 宏,通常许多编译器还有其他编译标志宏。

    #ifdef __cplusplus
           cout<<"c++";
    #else
           cout<<"c";
    #endif
    

    2.用nm命令查一下输出函数符号是否和函数名相同。c语言没有函数重载,所以c编译器编译的程序里,所有函数只有函数名对应的入口。而由于c++语言有重载函数的概念,如果只有函数名对应的入口,则会出现混淆,所以c++编译器编译的程序,应该是函数名+参数类型列表对应到入口。

7、函数重载的原理是什么?

名字改编:在linux g++编译器中,根据函数名字的字符数+形参类型和顺序的规则重命名记录在符号表中,从而产生不同的函数名,当外面的函数被调用时,便是根据这个记录的结果去寻找符合要求的函数名,进行调用。

8、inline函数与带参数的宏定义的区别?

  1. 内联函数在编译时展开,宏在预处理时展开。
  2. 内联函数直接嵌入到目标代码中,宏做简单的文本替换。
  3. 内联函数有类型检查,语法判断的功能,而宏没有。
  4. 内联函数时函数,宏不是。
  5. 宏定义时要注意书写(参数要加括号,避免产生歧义)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值