C/C++语言相关常见面试题总结

目录

const关键字的作用

volatile 关键字

#define和const有什么区别

decltype和auto的区别

extern 关键字的作用

如何避免野指针

C/C++中的类型转换以及使用场景

什么是RTTI?其原理是什么?

RTTI 的原理:

C++中引用和指针的区别

C++11用过哪些新特性

简述多态实现原理

虚函数表和虚函数表指针的创建时机

为什么基类的构造函数不能定义为虚函数?


const关键字的作用

思考题

  1. C语言中的const局部变量可以用指针间接修改, 说明可以取地址, 那此时局部变量放在什么区域?
  2. C语言中全局的const变量完全不可修改, 也不可以取地址、此时放在什么区域?
  3. C++中的const局部变量不可修改,可以取地址、此时又放在什么区域、底层是怎么实现的?
  4. 用const的好处是什么?

1.

放在那里? 应该是放在虚拟地址空间的栈存储区域,所以可以修改,可以取出地址。

const修饰的局部变量通常是存储在栈区域的,但编译器可能会根据实际情况进行优化,将其直接嵌入到目标代码中,而不需要分配栈上的存储空间。

2. 

应该是放置在虚拟地址空间的静态数据区域的.rodata只读数据区域,所以不可修改,并且禁止取地址、强行取地址会报段错误。

3.

可以取地址,说明取地址的时候将这个数据放入了栈区域一份。但是在生成目标代码时,编译器(编译阶段)将该变量的值直接嵌入到使用该变量的地方。这样,在程序执行时,这个变量的值就不会被修改,因为它在目标代码中已经被固定下来了,所以就算我们通过指针强行修改了给的变量所在的内存、但是编译阶段完成的直接的替换我们又改不了,所以此时真正的const实现其实是依赖于编译器的优化/强大。

4.

好处, 修饰变量,修饰函数,不可修改,检查提醒,节约内存的作用。

volatile 关键字

  • 给编译器说清楚,我这个关键字修饰的对象和函数是易变的,易挥发的。你不要进行优化。直接去读取内存。
  • 它甚至可以跟const联合使用,用了它修饰的变量,哪怕是const, 它也会告诉编译器不要在编译阶段直接就用类似于预处理阶段对define的替换处理一样给我直接替换成常量了(.rodata区域/代码段),而去给我实际读取内存。

#define和const有什么区别

  • 在C++中,两者的区别就在于数据替换时期不同,一个在预处理时期由预处理器完成,另一个是编译器由编译器完成数据值的直接替换。
  • 两者作用时期不同、处理工具不同,自然预处理器是很简单的,他不比编译器的复杂。预处理器简单的做替换,展开。编译器在编译阶段会做词法分析,语法分析,类型检查等等工作。
  • 所以相对来说,C++中推荐用const,应该就是有类型检查,更为安全一点。define太暴力了、出错了没法搞。

decltype和auto的区别

  • 首先两者都是类型推导的方式。只不过,一个是根据初始化的表达式来推导,另一个是主动给出表达式来推导。并且decltype可以准确的推导出类型,auto推导自动去除const、&、*这些附加属性

decltypeauto 是 C++11 引入的两个关键字,用于从表达式推导变量的类型,但它们之间有一些区别。

  1. decltype

    • decltype 是一个类型推导关键字,用于获取表达式的类型。
    • decltype 的结果类型与给定表达式的类型完全相同,包括 const 和引用等修饰符。
    • decltype 的语法形式为 decltype(expression),其中 expression 是需要推导类型的表达式。
    • 通常用于从变量、函数调用、表达式等处推导类型。
  2. auto

    • auto 也是一个类型推导关键字,用于自动推导变量的类型。
    • auto 的结果类型与表达式的类型相同,但会忽略掉顶层 const 和引用等修饰符。
    • auto 的语法形式为 auto,直接声明变量时使用。
    • 通常用于定义变量时,让编译器自动推导变量的类型,可以简化代码并提高可读性。

简而言之,decltype 用于获取表达式的确切类型,包括 const 和引用等修饰符;而 auto 用于自动推导变量的类型,会忽略掉顶层 const 和引用等修饰符。

extern 关键字的作用

void Test() {extern void Print(); // 声明一下外部的Print函数调用Print(); // 找不到}void Print() {printf("hahaha\n");
}int main() {Test();return 0;
}

也可以引用外部文件的函数,用extern 关键字显示说明一下这个函数,或者变量在外部文件或者区域已经定义好了,此处编译器你不用帮我报错,或者警告,它是定义好了的。

如何避免野指针

上述再补充一个及时跟新迭代器,迭代器失效本质上也就是插入,删除操作之后,之前的迭代器指向的节点发生了改变,所以我们需要及时跟新迭代器,避免操作失效的迭代器,其实本质上也是一个失效的指针.

迭代器失效的本质确实类似于野指针或非法操作内存的情况,因为迭代器失效意味着迭代器指向的位置在容器中不再有效,可能会指向已经释放的内存地址或者容器中的其他位置.

C/C++中的类型转换以及使用场景

  • C语言这个类型转换看似很爽,啥都能转,充分给足程序猿面子,但是转换不明确,限制太少了、而且没有错误检查,很容易出错。(太霸道了)
  • static_cast:说白了就是最基础的类型转换,也是最常用的类型转换。基本类型,子类指向父类的安全转换(上行转换)、void*和其他类型指针的转换。(最像C转换使用场合的转换)
  • const_cast:专门去除 const 或者 volatile 属性的。
  • dynamic_cast:用于父子类型之间的安全转换。编译器默认上行转换安全,下行转换进行类型检查。dynamic_cast 运算符在 C++ 中用于执行安全的向下转型(downcasting),并且在运行时进行类型检查。它主要用于在继承层次结构中的类之间进行转换,以及在执行转换之前进行类型检查。
  • 类型检查: 当使用 dynamic_cast 进行向下转型时,编译器会在运行时检查指针或引用指向的对象的实际类型是否与要转换到的类型相匹配。这种匹配性检查通过查看对象的虚拟表来实现。如果指向的对象实际上是要转换到的类型或其派生类的对象,则转换成功;否则,转换失败。
  • 失败处理

    • 如果 dynamic_cast 无法将基类指针或引用转换为派生类指针或引用,它将返回一个空指针(对于指针)或抛出一个 std::bad_cast 异常(对于引用)。
    • 如果进行指针类型转换时,指针为 null,则 dynamic_cast 也会返回 null。
  • 适用性

    • dynamic_cast 只能用于具有虚函数的类,因为它需要虚拟表来检查对象的实际类型。
    • dynamic_cast 只能用于多态类型(即基类或包含虚函数的类)之间的转换。
    • dynamic_cast 不适用于非多态类型之间的转换。

接着上面这个dynamic_cast 是运行时检测。

什么是RTTI?其原理是什么?

RTTI(Run-Time Type Information)是 C++ 提供的一种机制,用于在运行时获取对象的类型信息。RTTI 允许程序在运行时查询对象的类型,以便进行类型安全的操作,例如动态类型转换和异常处理。

RTTI功能体现在两个运算符上:typeid运算符和dynamic_cast运算符

  • typeid运算符,用于返回表达式的类型,可以通过基类的指针获取派生类的数据类型.

RTTI 的原理:

  1. 虚函数表(vtable): 在启用 RTTI 的情况下,C++ 编译器会为每个类生成一个虚函数表(vtable)。虚函数表是一个存储了指向虚拟函数的指针的数组,每个类都有自己的虚函数表。当一个类含有至少一个虚函数时,这个类就会拥有一个虚函数表。

  2. 类型信息对象(type_info): 对于每个类,编译器还会生成一个类型信息对象,其类型为 type_info。这个对象包含有关该类的类型信息,例如类名、类型大小等。在包含 RTTI 的编译器中,这个类型信息对象也包含指向虚函数表的指针。

  3. dynamic_cast 运算符的工作: 当使用 dynamic_cast 运算符进行向下转型时,编译器会查找源对象的类型信息对象,并在其中查找目标类型的信息。如果目标类型是源对象的基类或者与其兼容,则 dynamic_cast 可以成功转换;否则,转换失败。这种类型信息的查询是通过访问类型信息对象的虚函数表来实现的。

  4. typeid 运算符: C++ 还提供了 typeid 运算符,用于获取对象的类型信息。当使用 typeid 运算符时,编译器会生成代码来获取对象的类型信息对象,从而可以得到对象的类型信息。

总之,RTTI 的原理是通过在运行时为每个类生成虚函数表和类型信息对象,并通过这些信息来实现对对象的类型进行查询和检查。这使得在运行时能够进行类型安全的操作,如动态类型转换和异常处理。

C++中引用和指针的区别

  • 一般来说引用的必须初始化绑定一个对象,并且不可更改绑定,且不可绑空,使得引用相对指针更加安全一些。但是引用也会存在安全问题(也会悬空,尤其是在lambda表达式中捕获引用)

以下是一些需要注意的情况:

  1. 捕获局部变量的引用:如果 lambda 表达式捕获了一个局部变量的引用,并且该引用在 lambda 表达式执行时已经超出了其作用域,则会导致引用悬空。这种情况下,应该确保 lambda 表达式执行时捕获的引用仍然有效。

  2. 捕获指向堆内存的引用:如果 lambda 表达式捕获了指向堆内存的引用,而在 lambda 表达式执行时,堆内存已经被释放,那么捕获的引用将变为悬空。在使用指针或引用捕获对象时,应该确保对象的生命周期能够覆盖整个 lambda 表达式的执行过程。

为了避免引用悬空的问题,可以采取以下措施:

  1. 使用值捕获:如果捕获的对象在 lambda 表达式执行期间不会发生变化,并且对象的生命周期足够长,可以考虑使用值捕获而不是引用捕获。

  2. 小心捕获生命周期:确保捕获的引用或指针所引用的对象在 lambda 表达式执行期间保持有效。这可能需要在 lambda 表达式外部控制对象的生命周期,或者使用智能指针等机制来管理对象的生命周期。

  3. 避免悬空引用:在捕获引用时,要确保引用的对象在 lambda 表达式执行期间保持有效。如果无法保证引用的有效性,则应该避免捕获该引用。

C++11用过哪些新特性

简述多态实现原理

注意时间点差异:

  • 静态的多态是通过重载和模板技术实现的,是在编译时确定。
  • 动态的多态是用虚函数机制实现,是运行时确定。

虚函数表和虚函数表指针的创建时机

注意:

  • 虚函数表是属于整个类的,其中无非包含的虚函数的入口地址嘛,这些函数也是属于类的。
  • 虚函数表的创建时机和虚函数表指针初始化的时机是不一样的。一个是编译器创建、放在全局只读数据段,另外一个是在构造函数中初始化的这个vptr。类对象内部前面4个字节都存放的是这个vptr,故而都可以通过它找到虚函数表。

为什么基类的构造函数不能定义为虚函数?

因为虚表指针的初始化在构造函数中完成,由于此时虚表指针还没有完成初始化,所以基类的构造函数不能定义为虚函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/568444.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyCharm环境下Git与Gitee联动:本地与远程仓库操作实战及常见问题解决方案

写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 前言下载及安装GitGit的使用设置用户签名设置用户安全目录Git基本操作Git实操操作 Pyc…

libVLC 视频缩放

libvlc是一个常用的开源多媒体框架,它可以用来播放和处理各种类型的音频和视频文件。如果想要缩放视频,可以通过libvlc提供的API来实现。 //设置视频的缩放比例。 libvlc_video_set_scale() 以下是如何使用 libVLC 设置视频缩放的基本步骤:…

Leetcode算法题笔记(2)

目录 图论51. 岛屿数量解法一 52. 腐烂的橘子解法一 53. 课程表解法一 54. 实现 Trie (前缀树)解法一 回溯55. 全排列解法一 56. 子集解法一解法二 57. 电话号码的字母组合解法一 58. 组合总和解法一解法二 59. 括号生成解法一解法二 60. 单词搜索解法一 61. 分割回文串解法一 …

用户验证:Streamlit应用程序与Streamlit-Authenticator

写在前面 在数字化时代,数据安全和用户隐私越来越受到重视。对于使用Streamlit构建的Web应用程序来说,确保用户的安全身份验证是至关重要的。而Streamlit-Authenticator,作为一个专门为Streamlit应用程序设计的身份验证库,正成为保…

【C语言】linux内核pci_enable_device函数和_PCI_NOP宏

pci_enable_device 一、注释 static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) {struct pci_dev *bridge;int err;int i, bars 0;/** 此时电源状态可能是未知的,可能是由于新启动或者设备移除调用。* 因此获取当前的电源状态&…

51单片机入门:定时器与中断系统

定时器的介绍 定时器:51单片机的定时器属于单片机的内部资源,其电路的设计连接和运转均在单片机内部完成。根据单片机内部的时钟或者外部的脉冲信号对寄存器中的数据加1,定时器实质就是加1计数器。因为又可以定时又可以计数,又称…

力扣算题【第二期】

文章目录 1.反转链表1.1 算法题目1.2 算法思路1.3 代码实现 2.回文链表2.1 算法题目2.2 算法思路2.3 代码实现 1.反转链表 1.1 算法题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 1.2 算法思路 1.设置工作指针p,来遍历链表。 2.采…

JD Edwards 怎么编写和测试BSSV

BSSV对象发布到本地服务器 提示:只针对BSSV 程序名J开头的程序本地编写和发布测试 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 BSSV对象发布到本地服务器前言一、J程序有什么作用?二、1…

数学算法(算法竞赛、蓝桥杯)--分解质因数、唯一分解定理

1、B站视频链接&#xff1a;G07 分解质因数 唯一分解定理 试除法_哔哩哔哩_bilibili 题目链接&#xff1a;质因子分解 - 洛谷 #include <bits/stdc.h> using namespace std;int n; int a[100010];//质因子的个数void decompose(int x){for(int i2;i*i<x;i){//i增加&a…

每日必学Linux命令:mv命令

mv命令是move的缩写&#xff0c;可以用来移动文件或者将文件改名&#xff08;move (rename) files&#xff09;&#xff0c;是Linux系统下常用的命令&#xff0c;经常用来备份文件或者目录。 一&#xff0e;命令格式&#xff1a; mv [选项] 源文件或目录 目标文件或目录二&am…

Redis入门到实战-第十四弹

Redis实战热身Hyperloglogs篇 完整命令参考官网 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#xff09;&#xff0c;用作数据库、缓存、消息…

内网端口如何映射到外网?

内网端口映射到外网是一项重要的网络技术&#xff0c;它可以实现在任何网络环境下远程访问和管理内网设备。在复杂的网络环境中&#xff0c;内网设备通常无法直接被外网访问&#xff0c;而内网端口映射技术可以解决这个问题。本文将介绍一种名为【天联】的组网产品&#xff0c;…