《逆袭进大厂》之C++篇49问49答(绝对的干货),必须收藏
Posted 程序喵大人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《逆袭进大厂》之C++篇49问49答(绝对的干货),必须收藏相关的知识,希望对你有一定的参考价值。
最近C++技术交流群发现了很多水平很高的朋友,欢迎大家来加喵哥微信,进群一起讨论计算机知识!
程序喵大人微信
大家好,我是阿秀
答应你们的《逆袭进大厂》系列正式开始了。
好吧我说实话,这些都是我自己整理的秋招笔记,一把屎一把尿慢慢总结出来的那种,这些笔记可以说对我帮助良多。
它是在 github 上的 clone 下来的仓库笔记 + 自己看书理解到的知识点 + 网上相关问题的博客总结这几大基础上慢慢总结形成的,并不仅仅只是简单的收集整理,没有加入自己思考的笔记没有灵魂。
在接下来的十篇文章里我会陆陆续续将自己的秋招笔记整理出来,主要涉及C++、操作系统、计算机网络、mysql、Redis等知识点。
C++笔记实在太多,有足足6W多字之多,我打算分成两期文章
友情提示,这篇文章字数 3W+….
这篇文章能看完算我输!建议收藏本文,不开玩笑,真心建议。
这是本期的 C++ 八股文目录,看看你会哪些?
这是下期的 C++ 八股文目录,下期的要难一些。
闲言少叙,发车了
1、在main执行之前和之后执行的代码可能是什么?
main函数执行之前,主要就是初始化系统相关资源:
设置栈指针
初始化静态static变量和global全局变量,即.data段的内容
将未初始化部分的全局变量赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL等等,即.bss段的内容
全局对象初始化,在main之前调用构造函数,这是可能会执行前的一些代码
将main函数的参数argc,argv等传递给main函数,然后才真正运行main函数
main函数执行之后:
全局对象的析构函数会在main函数之后执行;
可以用 atexit 注册一个函数,它会在main 之后执行;
2、结构体内存对齐问题?
未特殊说明时,按结构体中size最大的成员对齐(若有double成员,按8字节对齐。)
3、指针和引用的区别
指针可以有多级,引用只有一级
指针可以为空,引用不能为NULL且在定义时必须初始化
指针在初始化后可以改变指向,而引用在初始化之后不可再改变
sizeof指针得到的是本指针的大小,sizeof引用得到的是引用所指向变量的大小
引用只是别名,不占用具体存储空间,只有声明没有定义;指针是具体变量,需要占用存储空间。
引用在声明时必须初始化为另一变量,一旦出现必须为typename refname &varname形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。
不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。
参考代码:
void test(int *p)
{
int a=1;
p=&a;
cout<<p<<" "<<*p<<endl;
}
int main(void)
{
int *p=NULL;
test(p);
if(p==NULL)
cout<<"指针p为NULL"<<endl;
return 0;
}
//运行结果为:
//0x22ff44 1
//指针p为NULL
void testPTR(int* p) {
int a = 12;
p = &a;
}
void testREFF(int& p) {
int a = 12;
p = a;
}
void main()
{
int a = 10;
int* b = &a;
testPTR(b);//改变指针指向,但是没改变指针的所指的内容
cout << a << endl;// 10
cout << *b << endl;// 10
a = 10;
testREFF(a);
cout << a << endl;//12
}
4、堆和栈的区别
申请方式不同:栈由系统自动分配;堆是自己申请和释放的。
申请效率不同:栈由系统分配,速度快,不会有碎片;堆由程序员分配,速度慢,且会有碎片。
形象的比喻
栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
《C++中堆(heap)和栈(stack)的区别》:https://blog.csdn.net/qq_34175893/article/details/83502412
5、区别以下指针类型?
int *p[10]
int (*p)[10]
int *p(int)
int (*p)(int)
int *p[10]表示指针数组,强调数组概念,是一个数组变量,数组大小为10,数组内每个元素都是指向int类型的指针变量。
int (*p)[10]表示数组指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10。
int *p(int)是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。
int (*p)(int)是函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的。
6、基类的虚函数表存放在内存的什么区,虚表指针vptr的初始化时间
首先整理一下虚函数表的特征:
虚函数表是全局共享的元素,即全局仅有一个,在编译时就构造完成
虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表,即虚函数表不是函数,不是程序代码,不可能存储在代码段
根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定,因此最有可能存在全局数据区,测试结果显示:
虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别
由于虚表指针vptr跟虚函数密不可分,对于有虚函数或者继承于拥有虚函数的基类,对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存在对象内存布局的最前面。
《虚函数表存放在哪里》:https://blog.csdn.net/u013270326/article/details/82830656
一般分为五个区域:栈区、堆区、函数区(存放函数体等二进制代码)、全局静态区、常量区
C++中虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区。
7、new / delete 与 malloc / free的异同
相同点
都可用于内存的动态申请和释放
不同点
前者是C++运算符,后者是C/C++语言标准库函数
new自动计算要分配的空间大小,malloc需要手工计算
new是类型安全的,malloc不是。例如:
int *p = new float[2]; //编译错误
int *p = (int*)malloc(2 * sizeof(double));//编译无错误
new调用名为operator new的标准库函数分配足够空间并调用相关对象的构造函数,delete对指针所指对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用内存。后者均没有相关调用
后者需要库文件支持,前者不用
new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象
8、new和delete是如何实现的?
new的实现过程是:首先调用名为operator new的标准库函数,分配足够大的原始为类型化的内存,以保存指定类型的一个对象;接下来运行该类型的一个构造函数,用指定初始化构造对象;最后返回指向新分配并构造后的的对象的指针
delete的实现过程:对指针指向的对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用内存
9、malloc和new的区别?
malloc和free是标准库函数,支持覆盖;new和delete是运算符,并且支持重载。
malloc仅仅分配内存空间,free仅仅回收空间,不具备调用构造函数和析构函数功能,用malloc分配空间存储类的对象存在风险;new和delete除了分配回收功能外,还会调用构造函数和析构函数。
malloc和free返回的是void类型指针(必须进行类型转换),new和delete返回的是具体类型指针。
delete和delete[]区别?
delete只会调用一次析构函数。
delete[]会调用数组中每个元素的析构函数。
10、宏定义和函数有何区别?
宏在编译时完成替换,之后被替换的文本参与编译,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;函数调用在运行时需要跳转到具体调用函数。
宏定义属于在结构中插入代码,没有返回值;函数调用具有返回值。
宏定义参数没有类型,不进行类型检查;函数参数具有类型,需要检查类型。
宏定义不要在最后加分号。
11、宏定义和typedef区别?
宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。
宏替换发生在编译阶段之前,属于文本插入替换;typedef是编译的一部分。
宏不检查类型;typedef会检查数据类型。
宏不是语句,不在在最后加分号;typedef是语句,要加分号标识结束。
注意对指针的操作,typedef char * p_char和#define p_char char *区别巨大。
12、变量声明和定义区别?
声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
相同变量可以在多处声明(外部变量extern),但只能在一处定义。
13、哪几种情况必须用到初始化成员列表?
初始化一个const成员。
初始化一个reference成员。
调用一个基类的构造函数,而该函数有一组参数。
调用一个数据成员对象的构造函数,而该函数有一组参数。
14、strlen和sizeof区别?
sizeof是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen是字符处理的库函数。
sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是' '的字符串。
因为sizeof值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。
int main(int argc, char const *argv[]){
const char* str = "name";
sizeof(str); // 取的是指针str的长度,是8
strlen(str); // 取的是这个字符串的长度,不包含结尾的