一、【阿里C++面试题】
1、如何初始化一个指针数组。
答案:
c++中的指针是一个很经典的用法,但是也是最容易出错的,比如定义了一个指针,必须对其进行初始化,不然这个指针指向的是一个未知的内存地址,后续对其操作的时候,会报错。这只是其次,最让人头疼的就是指针错误问题,往往编译的时候可以通过,在程序运行的时候,就会出现异常,如果对程序不是很熟悉,则不是很容易找到问题所在,我最近就遇到过很多这样的问题,定义了一个结构体指针,使用的时候忘记初始化,导致在后边使用的时候程序报异常。下面就总结一下c++指针初始化的一些方法,
以及我自己遇到的一些问题以及心得体会。
一.c++指针初始化的一般方法:
1.将一个已经在内存中存在变量的地址传递给定义的指针,这个指针就指向这个变量的内存地址(相同的数据类型),完成初始化。
比如:
int a=2; int *b=&a;
2.利用new开辟一块地址空间
struct test{ int a; int b; }*t; void main() { int c=0; test *t=new test(); c=t->a; }
使用 new开辟的空间,记得使用delete释放,因为new出来的是返回的堆的空间,堆的空间是不会自动释放的,存放变量的栈才会自动释放。delete释放其实只是释放了申请的这块内存空间,但是指针并没有没撤销,指针还是指向这块地址,但是不可用(靠人品吃饭的有可能可以用),是非法的。所以用delete释放掉一块堆内存时,应该自己手动将指针设置为NULL。
3.把指针设置为NULL或者0
这样做一般只是为了没有具体初始化的时候做的,这样避免了野指针,后面可以使用if(指针==NULL)来判断,然后再进行操作。
错题解析:首先明确一个概念,就是指向数组的指针,和存放指针的数组。 指向数组的指针:char (*array)[5];含义是一个指向存放5个字符的数组的指针。 存放指针的数组:char *array[5];含义是一个数组中存放了5个指向字符型数据的指针。 按照题意,我理解为初始化一个存放指针的数组,char *array[2]={“China”,”Beijing”};其含义是初始化了一个有两个指向字符型数据的指针的数组,这两个指针分别指向字符串”China”和”Beijing”。
2、关键字const是什么含意?
正确答案:
在标准C++中,这样定义的是一个常量,用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数。
const使用(类型):
1、用于指针的两种情况:const是一个左结合的类型修饰符.
int const*A;//A可变,A不可变
intconst A;//A不可变,*A可变
2、限定函数的传递值参数:
void function(const int Var);//传递过来的参数在函数内不可以改变.
3、限定函数返回值型.
const int function();//此时const无意义
const myclassname function();//函数返回自定义类型myclassname.
4、限定函数类型.
void function()const;//常成员函数,常成员函数是不能改变成员变量值的函数。
错题解析:我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可 以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这 个问题,我将问他一个附加的问题:下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整 型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型 数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: 1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理 其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
3、 什么是动态特性?
正确答案:
在绝大多数情况下,程序的功能是在编译的时候就确定下来的,我们称为静态特性。反之,如果程序的功能是在运行时刻才确定下来的,则称为动态特性。
动态特性是面向对象语言最强大的功能之一,因为它在语言层面上支持程序的可扩展性,而可扩展性是软件设计追求的重要目标之一。
c++虚函数、抽象基类、动态绑定、多态构成了出色的动态特性。
1.虚函数
假定几何形状的基类为Shape,其派生类有Circle、Rectangle、Ellipse等,每个派生类都能够绘制自己所代表的形状。不管派生类的形状如何,我们希望用统一的方式来调用绘制函数,最好是使用Shape定义的接口函数Draw(),并让程序在运行是动态地确定应该使用哪一个派生类的Draw()函数。
为了使这种行为可行,我们把基类Shape中的函数Draw()声明为虚函数,然后在派生类中重新定义Draw()使之绘制正确的形状,这种方法叫覆盖,虚函数的声明方法是在基类的函数原型之前加上关键之virtual。
一旦类的一个函数被声明为虚函数,那么其派生类的对应函数也自动成为虚函数,这样一级一级传递下去。
2.抽象基类
当我们把类看成是一种数据类型时,通常会认为该类肯定是要被实例为一个或多个对象的。但是在很多情况下,定义那些不能实例化出对象的类也是很有用的,这种类就称为抽象类。抽象类的唯一目的就是让其派生类继承并实现它的接口方法,因此它通常也被称为抽象基类。
如果将基类的虚函数声明为春虚函数,那么该类就被定义为了抽象基类。純虚函数是在声明时将其“初始化”为0的函数,例如:
class Shape { //Shape是抽象基类 public: virtual void Draw(void)=0; //Draw()为純虚函数 }
`抽象基类Shape的純虚函数Draw()根本不知道自己应该怎么绘制出一个“形状”来,具体功能必须有代表具体形状的派生类对应的Draw()函数来实现。
我们知道,函数名就是函数的地址,将一个函数初始化为0意味着函数的地址将为0,这就是在告诉编译器,不要为该函数编址,从而阻止该类的实例化行为。
抽象基类的主要用途是“接口与实现分离”;不仅要把数据成员(信息)隐藏起来,而且还要把实现完全隐藏起来,只留一些接口给外部调用。
错题解析:在绝大多数情况下, 程序的功能是在编译的时候就确定下来的, 我们称之为静态特性。 反之, 如果程序的功能是在运行时刻才能确定下来的, 则称之为动态特性。C++中, 虚函数,抽象基类, 动态绑定和多态构成了出色的动态特性。
4、基类的有1个虚函数,子类还需要申明为virtual吗?为什么。
正确答案:建议加上vitrual,代码是写给人看的
错题解析:不申明没有关系的。 不过,我总是喜欢显式申明,使得代码更加清晰。
5、在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?
正确答案:
c语言程序和c++程序在编译时,是相互独立的编译;在.c文件中,编译器将Add函数重命名为_Add; 而在.cpp文件中,编译器将用extern声明的函数重命名为(?Add@@YAHHH@Z);那么在链接阶段,在_mian函数中该Add函数被调用;
此时编译器带着(?Add@@YAHHH@Z)函数名进入test.c文件的编译文件中去寻找(?Add@@YAHHH@Z)函数,但是在test.c文件中这个Add函数被编译器重命名为_Add,由于在.cpp文件中和.c文件中同一个函数名被编译器重命名后的名字不一样;所以.cpp文件声明的外部函数找不到真正定义的地方;程序错误;
(2)使用extern “C” int Add(int,int); 外部函数—-正确
#include<iostream> using namespace std; extern "C" int Add(int,int); int main() { cout<<Add(50,50)<<endl; return 0; }
解释:
c++语言为了支持重载,对编译时函数的重命名规则进行更改,
使用extern “C” 的方式在c++程序中声明c语言文件中的函数,可以在编译时,告诉编译器使用C语言的规则对该函数的的函数名的进行重命名,这样在链接的时候,就可以顺利在.c文件中找到该函数;
错题解析:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。
6、如何定义Bool变量的TRUE和FALSE的值。
正确答案:
对于C语言,并不存在系统自带的bool类型和False和Ture的定义,如果要使用则必须自定义类型。
有两种常见的方法。
1、使用宏定义。
通过define来进行定义False和True。可以采用如下方式:
(1) 直接定义数值
#define False 0 #define True 1
(2) 通过逻辑判断定义
#define False (1!=1) #define True (1==1)
2、使用枚举定义。
这种方法可以同时定义类型BOOL
typedef enum { False, True }BOOL;
错题解析:不知道这个题有什么陷阱,写到现在神经已经大了,一般来说先要把TURE和FALSE给定义了,使用#define就可以: #define TURE 1 #define FALSE 0 如果有一个变量需要定义成bool型的,举个例子:bool a=TURE;就可以了。
7、内联函数INline和宏定义一起使用的区别。
正确答案:
1、宏定义不会进行类型的判断,只是单存的替换文本;
inline会对参数类型进行判断。
2、宏不是函数,inline修饰的是函数。
3、宏返回值不能强制转换成合适的类型,inline返回值可以。
错题解析:内联函数是在编译的时候已经做好将对应的函数代码替换嵌入到对应的位置,适用于代码较少的函数。 宏定义是简单的替换变量,如果定义的是有参数的函数形式,参数不做类型校验。
8、编写my_strcpy函数,实现与库函数strcpy类似的功能,不能使用任何库函数;
正确答案:
char *strcpy(char *strDest, const char *strSrc) { if ( strDest == NULL || strSrc == NULL) return NULL ; if ( strDest == strSrc) returnstrDest ; char *tempptr = strDest ; while( (*strDest++ = *strSrc++) != ‘’); returntempptr ; }
9、 完成程序,实现对数组的降序排序
#include void sort(int array[] ); int main() { int array[]={45,56,76,234,1,34,23,2,3}; //数字任//意给出 sort( array ); return 0; } void sort( int array[] ) {____________________________________ inti,j,k; for(i=1;i<=7;i++) { if(array[i]>array[i-1]) { k=ARRAY[i]; j=i-1; do { array[j+1]=array[j]; j– ; } while(k>array[j]&&j>=0); array[j+1]=k; } } —————————————————– }
10、ICMP是什么协议,处于哪一层?
正确答案:
Internet控制报文协议,处于网络层(IP层)
11、 C中static有什么作用
正确答案:
(1)隐藏。 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局
可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
(2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量。
(3)static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0×00,某些时候这一特点可以减少程序员的工作量。
12、
Void GetMemory2(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, “hello”); printf(str); }
请问运行Test函数会有什么样的结果?
正确答案:
可以运行
13、C++特点是什么,如何实现多态?画出基类和子类在内存中的相互关系。
正确答案:
多态的基础是继承,需要虚函数的支持,简单的多态是很简单的。 子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等
14、 C++中的什么是多态性? 是如何实现的?
正确答案:
多态性是面向对象程序设计语言继数据抽象和继承之后的第三个基本特征。它是在运行时出现的多态性通过派生类和虚函数实现。基类和派生类中使用同样的函数名, 完成不同的操作具体实现相隔离的另一类接口,即把" w h a t"从"h o w"分离开来。多态性提高了代码的组织性和可读性,虚函数则根据类型的不同来进行不同的隔离。
15、 关键字static的作用是什么?
正确答案:
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数 据和代码范围的好处和重要性。
16、#define MAX_LEN 500 char arry[MAX_LEN]; cin>>arry; 这段代码有问题吗?若有,请指出并修改;
正确答案:
有问题。头文件缺少。 #include
17、delete []arry 和 delete arry 一样吗?不一样请说明;
正确答案:
delete []arry 释放的是多个同一类型的地址空间 Delete[]arry 释放的是一个某种类型的地址空间
18、 多态的作用?
正确答案:
主要是两个:
1)隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
2)接口重用,为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
19、C语言的volatile的含义是什么。使用时会对编译器有什么暗示。
正确答案:
从词面上讲,volatile的意思是易变的,也就是说,在程序运行过程中,有一些变量可能会被莫名其妙的改变,而优化器为了节约时间,有时候不会重读这个变量的真实值,而是去读在寄存器的备份,这样的话,这个变量的真实值反而被优化器给“优化”掉了,用时髦的词说就是被“和谐”了。如果使用了这个修饰词,就是通知编译器别犯懒,老老实实去重新读一遍!可能我说的太“通俗”了,那么我引用一下“大师”的标准解释: volatile的本意是“易变的” 。 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化,但有可能会读脏数据。当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。 下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量 嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
20、 请简述以下两个for循环的优缺点
1)
for (i=0; i<n; i++) { if (condition) DoSomething(); else DoOtherthing(); }
2)
if (condition) { for (i=0; i<n; i++) DoSomething(); } else { for (i=0; i<n; i++)=“” dootherthing();=“” }=“”
正确答案:
1)优点:程序简洁。=“” 缺点:多执行了n-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。=“”
2)优点:循环的效率高。缺点:程序不简洁。="
二、【百度C++面试题】
1、 预处理器标识#error的目的是什么?
正确答案:
如果你不知道答案,请看参考文献1。
2、C语言的volatile的含义是什么。使用时会对编译器有什么暗示。
正确答案:
从词面上讲,volatile的意思是易变的,也就是说,在程序运行过程中,有一些变量可能会被莫名其妙的改变,而优化器为了节约时间,有时候不会重读这个变量的真实值,而是去读在寄存器的备份,这样的话,这个变量的真实值反而被优化器给“优化”掉了,用时髦的词说就是被“和谐”了。如果使用了这个修饰词,就是通知编译器别犯懒,老老实实去重新读一遍!可能我说的太“通俗”了,那么我引用一下“大师”的标准解释: volatile的本意是“易变的” 。 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化,但有可能会读脏数据。当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。 下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
3、 MFC中CString是类型安全类么?
正确答案:
不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换
4、内联函数INline和宏定义一起使用的区别。
正确答案:
内联函数是在编译的时候已经做好将对应的函数代码替换嵌入到对应的位置,适用于代码较少的函数。 宏定义是简单的替换变量,如果定义的是有参数的函数形式,参数不做类型校验。
5、C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?
正确答案:
栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理 堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上
6、DB事务处理的四个特性:
正确答案:
原子性,一致性,隔离性,持久性 就答对一个:一致性,
7、如何初始化一个指针数组。
正确答案:
首先明确一个概念,就是指向数组的指针,和存放指针的数组。 指向数组的指针:char (*array)[5];含义是一个指向存放5个字符的数组的指针。 存放指针的数组:char *array[5];含义是一个数组中存放了5个指向字符型数据的指针。 按照题意,我理解为初始化一个存放指针的数组,char *array[2]={“China”,”Beijing”};其含义是初始化了一个有两个指向字符型数据的指针的数组,这两个指针分别指向字符串”China”和”Beijing”。
8、 int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
正确答案:
相当于 i=j=4;i=k=8;i=l=16;i=m=32; 故最后i=32;
9、如何在C中初始化一个字符数组。
正确答案:
这个问题看似很简单,但是我们要将最简单的问题用最严谨的态度来对待。关键的地方:初始化、字符型、数组。最简单的方法是char array[];。这个问题看似解决了,但是在初始化上好像还欠缺点什么,个人认为:char array[5]={’1′,’2′,’3′,’4′,’5′};或者char array[5]={“12345″};或者char array[2][10]={“China”,”Beijing”};也许更符合“初始化”的意思。
10、参数传递有几种方式;实现多态参数传递采用什么方式,如果没有使用某种方式原因是什么;
正确答案:
传值,传指针或者引用
11、请填写BOOL , float, 指针变量 与“零值”比较的 if 语句。
正确答案:
这里“零值”可以是0, 0.0 , FALSE或者“空指针”。例如 int 变量 n 与“零值”比较的 if 语句为:
if ( n == 0 ) if ( n != 0 )
12、C++特点是什么,如何实现多态?画出基类和子类在内存中的相互关系。
正确答案:
多态的基础是继承,需要虚函数的支持,简单的多态是很简单的。 子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等
13、 什么是“引用”?申明和使用“引用”要注意哪些问题?
正确答案:
引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引 用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只 表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
14、触发器怎么工作的?
正确答案:
触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数据的处理必须符合由这些SQL 语句所定义的规则。
15、C也可以通过精心封装某些函数功能实现重用,那C++的类有什么优点吗,难道仅仅是为实现重用。
正确答案:
并不仅仅是这样的。 OOD,OOP从根本上改变了程序设计模式和设计思想,具备重大和深远的意义。 类的三大最基本的特征:封装,继承,多态.