c++程序员面试宝典之c++基础知识

一.const用途:

1、const是用来声明一个常量的,只读变量:当const修饰指针时,如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,不能*a=3操作,如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量,而指针所指向的内容不是常量,不能a++操作,const int* const a=&b;指针本身和指向的内容均为常量。

2、const修饰函数的输入参数:对于非内部数据类型的输入参数,将“值传递”的方式改为“const引用,目的提高效率”,如将void Func(A a)改为voidFun(const A&a),对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到高效率的目的,有降低了函数的可理解性。

3、const修饰函数的返回值:如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针,如const char*GetString(void),char*str=GetString()错误,const char*str=GetString()正确;如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值,函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。

4、const修饰成员函数:修饰类的成员函数,char get() const;在后面加const,被const修饰的成员函数代表不修改成员变量的值,会提高程序的健壮性。

5、const常量与#define宏定义的区别:1、define宏是在预处理阶段展开,const常量是编译运行阶段使用;2、const 常量有数据类型,而宏常量没有数据类型;3、编译器可以对前者进行类型安全检查。而对后者只进行字符替换;4、define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存,const常量会在内存中分配(可以是堆中也可以是栈中)。

二.static用途:

1、作用域隐藏。当一个工程有多个文件的时候,用static修饰的函数或变量只能够在本文件中可见,文件外不可见;

2、全局生命周期。用static修饰的变量或函数生命周期是全局的。被static修饰的变量存储在静态数据区;

3、函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

4、static修饰的变量默认初始化为0;

5、在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

6、在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

三.sizeof()

1、sizeof是运算符,并不是函数;

2、sizeof不能求得void类型的长度;

3、sizeof的用法是sizeof(参数),这个参数可以是数组,指针,类型,对象,甚至是函数,其值在编译的时候就计算好了,

4、sizeof可以对函数调用求大小,并且求得的大小等于函数返回类型的大小,但是不执行函数体。

5、sizeof指针变量与指针所指对象无关,32位是4,64位是8;

6、sizeof求得结构体(及其对象)的大小并不等于各个数据成员对象的大小之和(内存对齐)

7、一个空的数据类型,里面没有任何成员变量和成员函数,对该类型求sizeof,结果是1,当我们声明该类型的实例时,它必须在内存中占有一定的空间,否则无法使用这些实例,至于占用多少内存,由编译器决定,如果在该类型中添加一个构造函数和析构函数,再求sizeof,还是1,调用构造函数和析构函数只需要知道函数的地址即可,而这些地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息,如果把析构函数标记为虚函数,C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针,在32位机器上,一个指针占4字节空间,因此求sizeof得到4;如果是64位则为8。

四.sizeof与strlen的区别

1、sizeof是运算符,而strlen是函数strlen(char*);

2、sizeof的用法是sizeof(参数),这个参数可以是数组,指针,类型,对象,甚至是函数,其值在编译的时候就计算好了,而strlen的参数必须是字符型指针(char*),其值必须在函数运行的时候才能计算出来;

3、strlen(char*)函数求的是字符串的实际长度,它求的方法是从开始遇到第一个’\0’,如果你只定义没有给它赋初值,这个结果是不定的,它从指针指向的首地址一直找下去,直到遇到’\0’为止。 如char aa[10];strlen(aa);//结果是不定的  sizeof(aa); //结果是10

4、当数组作为参数传递给函数的时候,传的是指针,而不是数组,传递数组的首地址;比如:

char aaa[]={"LRPLRP"};

int a,b;

fun(aaa,a,b);

void fun(char aaa[],int&a,int&b)

{

  a=strlen(aaa);

  b=sizeof(aaa);

}

结果是:a=6,b=4.在函数内部,虽然aaa转化成指针,但是strlen还是测试aaa开始直到’\0’为止的字符串的长度,而sizeof则是测指针的长度。

五.内存对齐

1、原因:现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐,若不对齐,则会在存取效率读取效率上下降。

2、规则:默认对齐系数是8,可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,偏移位为min(结构中最大数据成员长度,对齐系数)的最小整数倍。

3、即使是同样数目与数量的数据成员,在摆放的顺序不同时struct的大小也会不同,比如

struct{

    char a;

    int b;

    char d;

    short c;

}myStruct;      sizeof(myStruct)结果为12;

struct{

    char a;

    char d;

    short c;

    int b;

}myStruct;      sizeof(myStruct)结果为8;

4、存在内存对齐的情况下,不能使用函数memcmp来判断两个结构体是否相等:memcmp函数是逐个字节进行比较的,而struct存在字节对齐,字节对齐时补的字节内容是随机的,会产生垃圾值,所以无法比较,只能逐个比较其成员。

六.memcmp与strncmp区别

对于memcmp(),如果两个字符串相同而且count大于字符串长度的话,memcmp不会在\0处停下来,会继续比较\0后面的内存单元,直到_res不为零或者达到count次数。

对于strncmp(),比较必定会在最短的字符串的末尾停下来,即使count还未为零。

所以,如果想使用memcmp比较字符串,要保证count不能超过最短字符串的长度,否则结果有可能是错误的。

七.指针和引用区别

1、引用是变量的一个别名,内部实现是只读指针

2、引用只能在初始化时被赋值,其他时候值不能被改变,指针的值可以在任何时候被改变

3、引用不能为NULL,指针可以为NULL

4、引用变量内存单元保存的是被引用变量的地址

5、“sizeof 引用" = 指向变量的大小 , "sizeof 指针"= 指针本身的大小

6、指针可以有多级,但是引用只能是一级

八.C++传值,传引用,传地址的区别

1、传值,是把实参的值赋值给行参,那么对行参的修改,不会影响实参的值;

2、传地址,是传值的一种特殊方式,只是他传递的是地址,不是普通的如int,那么传地址以后,实参和行参都指向同一个对象;

3、传引用,真正的以地址的方式传递参数,传递以后,行参和实参都是同一个对象,只是他们名字不同而已, 对行参的修改将影响实参的值;

4、无论你传值还是传指针,函数都会生成一个临时变量, 但传引用时,不会生成临时变量,

5、传指针时,在函数中只能修改指针所指的内容*p,但不能改变指针本身的指向,即指针地址,若要修改,则需要使用指针的指针或者指针引用。

九.malloc和new的区别

1、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符;

2、申请的内存所在位置:new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存;

3、new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL,需要添加if(a==NULL)进行判断;

4、是否需要指定内存大小:使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸;

5、是否调用构造函数/析构函数:new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会;

6、new与malloc是否可以相互调用:operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new;

7、是否可以被重载:opeartor new /operator delete可以被重载,而malloc/free并不允许重载

8、对数组的处理:C++提供了new[]与delete[]来专门处理数组类型,malloc如果要动态分配一个数组的内存,还需要我们手动自定数组的大小,如int * ptr=(int *)malloc(sizeof(int)* 10);//分配一个10个int元素的数组。

十.C++内存分配

1、栈区: 由编译器自动分配释放,存放函数的参数值,局部变量的值等;

2、堆区: 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收;

3、自由存储区:由new分配的内存块,和堆十分相似;

4、全局/静态存储区:全局变量和静态变量被分配到同一块内存中,程序编译时已经分配好;

5、常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改;

十一.堆栈区别:

1、申请方式:栈由系统分配;堆要程序员自己申请并指明大小;

2、申请大小限制:栈是一块连续的内存区域,栈的容量是系统预先规定好的,如果申请的空间超过栈的剩余空间,将会提示overflow,能从栈获得的空间较小;堆是不连续的内存区域,类似与链表,因此堆获得的空间比较灵活,也比较大;

3、申请效率:栈由系统自动分配,速度较快,但程序员是无法控制的;堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

十二.线程和进程

1、进程是表示资源分配的基本单位,又是调度运行的基本单位;线程是cpu调度,或者说是程序执行的最小单位;

2、线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程;

3、系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。

4、线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),而进程之间的通信只能通过进程通信的方式进行。

5、优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。

6、进程之间不会互相影响,一个线程挂掉将导致整个进程挂掉。

进程间的通信方式

管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件

十三.空悬指针和野指针的区别

1、空悬指针:它曾经指向一个有效地址,但是现在不再指向有效地址,其实就是原来的那块地址不能通过这个指针区访问了,通常是因为指针所指的内存单位被释放了并且不再有效了;

2、野指针:它没有被正确的初始化于是指向一个随机的内存地址。存在野指针是一个严重的错误。

十四.内存泄露与内存溢出

1、内存泄露是指程序中一块不再使用的内存没有被释放,造成内存保持占用状态,使操作系统不能将内存分配给其它的程序(进程)。内存泄露不是一个立即会引发故障的错误,但是它将消耗系统内存;

2、内存溢出指你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,就是溢出;

3、检测是否存在内存泄漏问题

STEP1,在程序中包括以下语句:#define _CRTDBG_MAP_ALLOC  #include  #include

STEP2, 在添加了上述语句之后,可以通过在程序中包括以下语句_CrtDumpMemoryLeaks();

十五.C++是不是类型安全的?

答:不是,两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

十六. main函数执行以前,还会执行什么代码?

全局对象的构造函数会在main 函数之前执行。

十七.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

1、生命周期不同:全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

2、使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。

3、操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。

十八.C++中宏与内联函数

共同点:都是通过避免被调用的开销来提高执行效率,就是都不会引起函数的调用,都是在调用函数的位置将函数体展开。

区别:

1、宏是在编译器预处理的时候将函数展开的;而inline函数则是在编译的时候展开的; 

2、使用宏很容易产生错误,为了避免这些,在定义的时候我们是将里面每个参数括起来,最后还要一起再括起来,而inline函数则没有必要;

3、宏的参数检查比较弱,一个函数可能实用于int类型,也有可能实用于float类型;而inline函数则有较强的类型检查,实用于int就只能是int,如float想用的话,就要靠template了。

十九.简述extern"C"的作用

extern关键字:表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

extern "C"的目的:实现类C和C++的混合编程。在C++源文件中的语句前面加上extern "C",表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。这样在类C的代码中就可以调用C++的函数或者变量等。

二十.c++11新特性之auto

1、作用:使用auto关键字来要求编译器对变量的类型进行自动推导;

2、用auto声明的变量必须初始化

3、函数和模板参数不能被声明为auto

4、定义在堆上的变量,使用了auto的表达式必须被初始化

5、优势:1)拥有初始化表达式的复杂类型变量声明时简化代码;2)可以避免类型声明时的麻烦而且避免类型声明时的错误;但是auto不能解决所有的精度问题。

你可能感兴趣的:(c++程序员面试宝典之c++基础知识)