c++面试知识整理

C++基础部分

1. 基础知识

1.1 内存

1.1.0 内存四区

意义在于:赋予其不同的生命周期,给编程带来更大的灵活性

运行前
代码区:存放函数体的二进制代码,由操作系统管理
共享的
只读的:防止程序意外修改其指令
全局区:存放全局变量和静态变量以及常量,结束后由系统释放
全局区还包括常量区(字符串常量,const修饰的全局常量)
运行后
栈区:由编译器自动分配和释放,存放函数体的参数值、局部变量等
不能返回局部变量的地址,当离开作用域后,开辟在栈区的局部变量会被编译器自动回收
堆区:由程序员分配和释放,若不释放,程序结束后由操作系统释放
分全局堆和局部堆
全局堆就是所有没有分配的空间,局部堆就是用户分配的空间
堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆

1.1.1 简述C、C++程序编译的内存分配情况

从静态存储区域分配:
内存在程序 编译 时 就已 经 分配 好,这块内 存在 程序 的整 个运行 期间 都存在。速度快不容易出错,因为有系统会善后。例如全局变量, static 变量, 常量字符串等。
在栈上分配:
在执行函数时, 函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中, 效率很高, 但是 分配的内存容量有限 。大小为2M。
从堆上分配:
即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏 ,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块 。

1.1.2 分配函数与释放函数

C:malloc、calloc、realloc / free
C++:new / delete
大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等

1.1.2.1 malloc / free

malloc函数向内存申请一块连续可用的空间
开辟成功则返回一个指向该空间的void* 型指针,所以需要对其进行强制类型转换,转换成我们想要的类型
开辟失败则返回 NULL,所以一定要对malloc的返回值进行检查
free 用来释放动态开辟的内存,而不是释放指针

int* ptr = NULL;
ptr = (int*)malloc(1000*sizeof(int));//开辟一千个int大小的内存,并强制类型转换
if(NULL == ptr){
   
	exit(1);
}
free(ptr);
ptr = NULL;

释放只能一次,如果释放两次及两次以上会出现错误
释放空指针例外,释放空指针其实也等于什么都没做,所以释放空指针释放多少次都没有问题

1.1.2.2 new / delete
new分配内存步骤

调用operator new 函数
调用相应的构造函数构造对象,并传入初值
对象构造完成后,返回一个指向该对象的指针
delete释放内存步骤

调用对象的析构函数
调用operator delete 函数释放内存空间

//开辟变量
int* a = new int(10);
delete a;

//开辟数组
int* arr = new int[10];
delete[] arr;

1.1.2.3 new/delete 与 malloc/free 区别
开辟位置
严格来说,malloc动态开辟的内存在堆区,new开辟的叫做自用存储区
若不重载new操作符,c++编译器一般默认使用堆来实现自用存储,此时等价于堆区
特别:new可以不为对象分配内存
重载
new、delete是操作符,可以重载,只能在C++ 中使用。 malloc、free 是函数,可以覆盖,C、C++ 中都可以使用。
是否调用构造与析构函数
new 可以调用对象的构造函数,对应的delete 调用相应的析构函数。malloc 仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数
是否需要指定内存大小
malloc 需要显式指出开辟内存的大小,new 无需指定,编译器会自动计算
返回值类型
new返回的是某种数据类型指针,malloc返回的是void 指针,new比malloc更安全
new内存分配失败时,会抛出bac_alloc异常,不会返回NULL;malloc开辟内存失败会返回NULL指针,所以需要判断
1.1.2.4 calloc 、realloc
calloc(number,size):为number个大小为size的元素开辟一块空间,并把每个字节初始化为0
realloc(内存地址,大小):用于调整申请的空间大小

1.1.2.5 在C++中,使用malloc申请的内存能否通过delete释放?使用new申请的内存能否用free?
不能,malloc/free主要为了兼容C,new和 delete 完全可以取代malloc/free的。malloc/free 的操作对象都是必须明确大小的。而且不能用在动态类上。new 和 delete会自动进行类型检查和大小 ,malloc/free不能执行构造函数与析构函数 ,所 以动态对象它是不行的。当然从理论上说使用malloc 申请的内存是可以通过delete释放的 。不过一般不这样写的。而且也不能保证每个C++的运行时都能正常

1.2 预编译

1.2.1 头文件 < > < > <> “ “ ” ” 的问题

#include < >:只搜索系统目录,不会搜索本地目录
#include " “:首先搜索本地目录,若找不到才会搜索系统目录
#include<>相较于#include” " 快一些

1.2.2 c o n s t const const 与 # d e f i n e define define 相比有什么优点

const 常量有数据类型,而宏常量没有数据类型,编译器可以对前者进行安全检查。对后者只进行字符替换,没有安全类型检查,并且在字符替换可能会产生意想不到的错误
有些集成化的调试工具可用对const进行调试,但是不能对宏常量进行调试

1.3 宏,内联函数

1.3.1 内联函数

定义:在函数定义体前加入关键字inline,使函数成为内联函数
增加空间消耗换取效率提高,这点与宏一样
内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用

void fun(int x,int y);
inline void fun(int x,int y)//必须放在定义体前面,不能放在声明前面
{
   

}

适用情况
一个函数不断被重复调用
函数只有简单几行,且函数内不包括for、while、switch语句

1.3.2 内联函数与宏的差别

内联函数要做类型检查,而宏不需要
宏是在代码处不加任何验证的简单替代,而内联函数是将代码直接插入到调用处,而减少了普通函数调用时的资源消耗

1.3.3 写一个 “标准”宏 m i n min min

#define min(a,b)((a)<=(b)?(a):(b))

特别注意括号

1.3.4 t y p e d e f typedef typedef d e f i n e define define 有什么区别

用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义常量,以及书写复杂使用频繁的宏。
执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
作用域不同:typedef 有作用域限定。define 不受作用域约束,只要是在define 声明后的引用都是正确的。
对指针的操作不同:typedef 和define 定义的指针时有很大的区别。
注意:typedef 定义是语句, 因为句尾要加上分号。 而define不是语句,千万不能在句尾加分号

1.4 指针

1.4.1 指针常量和常量指针

常量:const int p = a;
常量指针:const int* p = &a; 、 int const *p = &a;
指针常量:int * const p = &a;
常量指针常量:const int * const p = &a;

const象征内容,* 象征地址

指针常量定义必须初始化

谁在前就先读,谁就不许变

常量指针:const修饰的是指针,指针指向可以改变,但是指针指向的值不能改变

指针常量:修饰的是常量,指针指向不可改变,但是在指针指向的值可以改变

常量指针常量:指针指向和指针指向的值均不可改变

int a = 10;
int b = 10;
//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int * p1 = &a;
p1 = &b; //正确
//*p1 = 100; 报错
//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int * const p2 = &a;
//p2 = &b; //错误
*p2 = 100; //正确
  //const既修饰指针又修饰常量
const int * const p3 = &a;
//p3 = &b; //错误
//*p3 = 100; //错误

1.4.2 指针函数和函数指针

  1. 指针函数:类型说明符 * 函数名(参数);
    是一个函数,返回一个指针,实际上就是返回一个地址给调用函数
    在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。
    也可以将其返回值设为void * 类型,调用时强制转换返回值为自己想要的类型
  2. 函数指针:类型说明符 (* 函数名)(参数) ;
    int (*FunPointerName)(int a,int b);
    是一个指针,指向函数的指针,包含了函数的地址,可以用它来调用函数,本质是一个指针变量,该指针指向这个函数
    把函数地址赋值给函数指针
    FunPoniterName = &FunctionName;
    FunPoniterName = FunctionName;
    调用函数指针
    x = (*FunPointerName)(参数);
    x = FunPointerName(参数);
#include <iostream>
using namespace std;
int add(int a,int b){
   
    return a + b;
}
int main(){
   
    int (*fun)(int a,int b);
    fun = add;
    cout << fun(10,20) << endl;
}

1.4.3 指针数组数组指针

指针数组:int *a[10];
是一个数组,a[ ]里面存的是地址
数组指针:int (*a)[10];
是一个指针,指向整个数组

1.4.4 函数传参

函数传参的三种方式:值传递,地址传递,引用传递

//值传递:就是函数调用时实参将数值传入给形参
//值传递时,如果形参发生,并不会影响实参
void swap01(int a,int b){
   
	int temp = a;
	a = b;
	b = temp;
}
//地址传递:利用指针作函数参数,可以修改实参的值
void swap02(int* a,int* b){
   
	int temp = *a;
	*a = *b;
	*b = temp;
}
//引用传递
//通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单
void swap03(int& a;int& b){
   
	int temp = a;
	a = b;
	b = temp;
}

1.4.5 一些定义

定义 说明
int a 一个整型数
int *a 一个指向整型的指针
int **a 一个指向指针的指针,它指向的指针是一个整数类型
int a[10] 一个有10个整型的数组
int *a[10] 指针数组:一个有10个指针的数组,指针指向整型
int (*a)[10] 数组指针:一个指向有10个整型数数组的指针
int (*a)(int) 函数指针:一个指向函数的指针,该函数有一个整型参数,并返回一个整型
int (*a[10])(int) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型

1.4.6 指针与引用的区别

指针有自己的一块空间,而引用只是一个别名,所以不能创建引用的引用,引用必须初始化,而指针可用为空;
使用sizeof看一个指针的大小是4,而引用的大小则是被引用对象的大小;
指针和引用使用++运算符的意义不一样;引用自增自减时,是引用所代表的空间的值发生变化,而指针自增自减时是指针指向的位置发生变化
作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
可以有const指针,但是没有const引用;
指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被改变;
指针可以有多级指针(**p),而引用止于一级;
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
引用本身不是一种数据类型,因此引用本身并不占存储单元,系统也不给引用分配存储单元,不能建立数组的引用

1.4.7 t h i s this this指针

this指针本质上是一个函数参数,只是编译器隐藏起形式的,语法层面上的参数
this 只能在成员函数中使用,全局函数和静态函数(属于类,不属于对象)都不能使用this
this 在成员函数的开始前构造,在成员的结束后清除。调用类成员函数时,编译器将类的指针作为参数传递进去
用途:
当形参与成员变量同名时,可以用this指针来区分
在类的非静态成员函数中返回对象本身,可用return *this;
this 指针并不占用对象空间,所以成员函数的参数,不管是不是隐含的,都不会占用对象空间,只会占用参数传递时的栈空间,或者直接占用一个寄存器
this 会因编译器不同而有不同的存放位置,可能是堆、栈、也可能是寄存器
this 指针只有在成员函数中才有定义,不能通过对象使用this指针,无法知道一个对象的this指针位置(只有在成员函数里才有this指针的位置,可通过&this获取)

1.4.8 指针和句柄

句柄和指针其实是两个截然不同的概念,window系统用句柄标记系统资源,隐藏系统的信息,它一个一个32bit的整数
而指针则标记某个物理内存地址,两者概念不同

1.4.9 如何避免“野指针”

指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向NULL。
指针p被free或者delete之后,没有置为NULL。解决办法:指针指向的内存空间被释放后指针应该指向NULL。
指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。

1.4.10 空指针与迷途指针区别

当delete一个指针的时候,实际上仅仅是让编译器释放内存,但指针本身依然存在,此时他就是一个迷途指针
可令ptr = 0; 使迷途指针变为空指针

1.5 c o n s t const const

任何不会修改数据成员的函数都应该声明为const 类型
在参数中使用const应该使用引用或指针,而不是一般的对象实例
除了重载操作符外一般不要将返回值类型定为对某个对象的const引用

1.5.1 c o n s t const const 使用

const使用:定义常量、修饰函数参数、修饰函数返回值
const 修饰类的成员变量,表示成员变量,不能被修改
如果const构成函数重载,const对象只能调用const函数,非const对象优先调用非const函数
const 函数只能调用const函数,非const函数可以调用const函数
类体外定义的const成员函数,在定义和声明处都需要const修饰符

1.5.2 c o n s t const const 作用

1.5.3 如何修改 c o n s t const const成员函数

用mutable修饰成员变量名后,就可以修改类成员变量

1.5.4 将 c o n s t const const类型转化为非 c o n s t const const类型

采用const_cast 进行转换。
用法:const_cast <type_id> (expression)

1.6 s i z e o f sizeof sizeof

数据对齐原则:是指数据所在的内存地址必须是该数据长度的整数倍。

1.6.1 s i z e o f sizeof sizeof s t r l e n strlen strlen 的区别

sizeof是一个操作符,strlen是库函数。
sizeof的参数可以是数据的类型,也可以是变量、函数;而strlen只能用char*做参数且且以结尾为‘\0’的字符串。
编译器在编译时就计算出了sizeof的结果,而strlen函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。
数组做sizeof的参数不退化,传递给strlen就退化为指针了
sizeof不能返回被动态分配的数组或外部的数组的尺寸
sizeof不能作用于函数类型,不完全类型或位字段,不完全类型是指具有未知存储大小数据的类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等

1.6.2 s i z e o f sizeof sizeof 的使用场合

其中一个主要用途就是与存储分配和I/O系统那样的例程通信
可以查看某种类型的对象在内存中所占的单元字节
在动态分配一个对象时,可以让系统知道要分配多少内存
便于一些类型的扩充。在window中有很多结构类型就有一个专用的字段来存放该类型的字节大小
如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小

1.7 强制类型转换运算符

static_cast

用于非多态类型的转换
不执行运行时类型检查(转换安全性不如 dynamic_cast)
通常用于转换数值数据类型(如 float -> int)
可以在整个类层次结构中移动指针,子类转化为父类安全(向上转换),父类转化为子类不安全(因为子类可能有不在父类的字段或方法)
用于各种隐式转换,比如非const转const,void*转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知
dynamic_cast

用于多态类型的转换,只能用于含有虚函数的类
执行行运行时类型检查
只适用于指针或引用
对不明确的指针的转换将失败(返回 nullptr),但不引发异常
可以在整个类层次结构中移动指针,包括向上转换、向下转换
const_cast

用于将const变量转为非const
用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 ) reinterpret_cast
用于位的

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LV小猪精

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值