程序员面试题整理非算法题.docx
- 文档编号:3566265
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:79
- 大小:873.27KB
程序员面试题整理非算法题.docx
《程序员面试题整理非算法题.docx》由会员分享,可在线阅读,更多相关《程序员面试题整理非算法题.docx(79页珍藏版)》请在冰豆网上搜索。
程序员面试题整理非算法题
1.设置地址为0x67a9的整型变量的值为0xaa66
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;
说明:
这道题就是强制类型转换的典型例子,无论在什么平台地址长度和整型数据的长度是一样的,即一个整型数据可以强制转换成地址指针类型,只要有意义即可。
2.函数传参获取数组长度的方法
template
voidprintlen(T(&c)[N])
{
cout<<"c[]length:
"< cout< } intmain(intargc,char*argv[]) { intp[]={1,2,3,4,5}; printlen(p); } 这是一种非类型模板形参,在需要常量表达式的时候可使用非类型模板形参。 如上例,当调用printlen函数时编译器从数组实参中读取intN的值 3.memset和memcpy #include memcpy 原型: externvoid*memcpy(void*dest,void*src,unsignedintcount); 功能: 由src所指内存区域复制count个字节到dest所指内存区域。 说明: src和dest所指内存区域不能重叠,函数返回指向dest的指针。 memset 包含: externvoid*memset(void*buffer,intc,intcount); 功能: 把buffer所指内存区域的前count个字节设置成字符c。 说明: 返回指向buffer的指针。 4.C++的空类有哪些成员函数 缺省构造函数。 缺省拷贝构造函数。 缺省析构函数。 缺省赋值运算符。 缺省取址运算符。 缺省取址运算符const。 5.写出floatx与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001) 6.Internet采用哪种网络协议? 该协议的主要层次结构? Tcp/Ip协议 主要层次结构为: 应用层/传输层/网络层/数据链路层/物理层 7.Internet物理地址和IP地址转换采用什么协议? ARP(AddressResolutionProtocol)(地址解析協議) 8.IP地址的编码分为哪俩部分? IP地址由两部分组成,网络号和主机号。 不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。 9.变量的声明和定义有什么区别 为变量分配地址和存储空间的称为定义,不分配地址的称为声明。 一个变量可以在多个地方声明,但是只在一个地方定义。 加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。 10.sizeof和strlen的区别 sizeof和strlen有以下区别: sizeof是一个操作符,strlen是库函数。 sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。 编译器在编译时就计算出了sizeof的结果。 而strlen函数必须在运行时才能计算出来。 并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。 数组做sizeof的参数不退化,传递给strlen就退化为指针了。 11.C中的malloc和C++中的new有什么区别 malloc和new有以下不同: (1)new、delete是操作符,可以重载,只能在C++中使用。 (2)malloc、free是函数,可以覆盖,C、C++中都可以使用。 (3)new可以调用对象的构造函数,对应的delete调用相应的析构函数。 (4)malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数 (5)new、delete返回的是某种数据类型指针,malloc、free返回的是void指针 12.一个指针可以是volatile吗 volatile类似于大家所熟知的const也是一个类型修饰符。 volatile是给编译器的指示来说明对它所修饰的对象不应该执行优化。 volatile的作用就是用来进行多线程编程。 可以,因为指针和普通变量一样,有时也有变化程序的不可控性。 常见例: 子中断服务子程序修改一个指向一个buffer的指针时,必须用volatile来修饰这个指针 13.static有什么用途? 对于局部变量改变其生存期,对于全局变量改变其可见性 主要从两个方面考虑: (1)静态存储: a.函数体内static变量的作用范围为该函数体,该内存在静态数据区上分配且只被分配一次,下次再调用该函数时依然维持上一次的值。 b.类中的static成员变量属于整个类所有,对类的所有对象只有一份拷贝 c.在类中的static成员函数属于整个类所拥有,这个函数不接受this指针,因而只能访问类的static成员变量。 (2)控制名字可见性 a.在模块内的static全局变量可以被模块内的所有函数访问,但是不能被模块外的其他函数访问 b.在模块内的static成员函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内。 14.引用与指针有什么区别 1)引用必须被初始化,指针不必。 2)引用初始化以后不能被改变,指针可以改变所指的对象。 3)不存在指向空值的引用,但是存在指向空值的指针。 15.描述实时系统的基本特性 在特定时间内完成特定的任务,实时性与可靠性。 16.简述C、C++程序编译的内存分配情况 C、C++中内存分配方式可以分为三种: (1)从静态存储区域分配: 内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。 速度快、不容易出错,因为有系统会善后。 例如全局变量,static变量等。 (2)在栈上分配: 在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3)从堆上分配: 即动态内存分配。 程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在何时用free或delete释放内存。 动态内存的生存期由程序员决定,使用非常灵活。 如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。 一个C、C++程序编译时内存分为5大存储区: 堆区、栈区、全局区、文字常量区、程序代码区 17.谈谈你对拷贝构造函数和赋值运算符的认识 拷贝构造函数和赋值运算符重载有以下两个不同之处: (1)拷贝构造函数生成新的类对象,而赋值运算符不能。 (2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。 而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉 注意: 当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。 18.不能被继承的类 (1)构造函数private 此时我们可以通过static成员函数获得该类的实例(我们只能得到位于堆上的实例) classFinalClass1 { public: staticFinalClass1*GetInstance() { returnnewFinalClass1; } staticvoidDeleteInstance(FinalClass1*pInstance) { deletepInstance; pInstance=0; } private: FinalClass1(){} ~FinalClass1(){} }; (2) 想法: 实现一个和一般类除了不能被继承之外其他用法都一样的类 template { friendT; private: MakeFinal(){} ~MakeFinal(){} }; classFinalClass2: virtualpublicMakeFinal { public: FinalClass2(){} ~FinalClass2(){} }; 此时FinalClass2就不能再被继承了,由于类FinalClass2是从类MakeFinal (虚继承的一个特征就是虚基类的构造函数由最终子类负责构造)非常遗憾的是,Try不是MakeFinal 基于上面的分析,试图从FinalClass2继承的类,一旦实例化,都会导致编译错误,因此是FinalClass2不能被继承。 这就满足了我们设计要求。 19.访问基类的私有虚函数 很好的题目,借这个题目学习虚函数。 解题思路就是: 虽然不能通过对象调用这个虚函数,但是因为虚函数会在子类的那段内存中保存虚函数表的入口地址,拿到虚函数的地址就可以转换成函数指针来调用他。 下面主要解释这一行代码: pFun=(Fun)*((int*)*(int*)(&b)+i); (1)在子类内存空间中最开始(就是子类对象的地址&b)的地方存了一个指向虚函数表的指针(这里的最开始的说法应该是大多数编译器的做法),为了获得这个指向虚函数表的指针,需要做这个操作*(int*)&b,解释: 对象b的地址强制转换成一个int指针(因为地址都是整型数),然后再解引用该指针获得虚表的指针值。 (2)再做一次(int*)获得虚表首地址的值,然后+i,每次+1都指向下一个虚函数的入口地址 (3)最后(Fun),将虚函数入口地址强制转换为函数指针,用函数指针来完成这个私有虚函数的调用。 #include classA { virtualvoidg(){cout<<"A: : g"< private: virtualvoidf(){cout<<"A: : f"< }; classB: publicA { voidg(){cout<<"B: : g"< virtualvoidh(){cout<<"B: : h"< }; typedefvoid(*Fun)(void); voidmain() { Bb; FunpFun; for(inti=0;i<3;i++) { pFun=(Fun)*((int*)*(int*)(&b)+i); pFun(); } } 20.类成员函数的重载、重写和隐藏 成员函数被重载的特征 (1)相同的范围(在同一个类中); (2)函数名字相同; (3)参数不同; (4)virtual关键字可有可无。 覆盖是指派生类函数覆盖基类函数,特征是 (1)不同的范围(分别位于派生类与基类); (2)函数名字相同; (3)参数相同; (4)基类函数必须有virtual关键字。 “隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下 (1)如果派生类的函数与基类的函数同名,但是参数不同。 此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。 (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。 此时,基类的函数被隐藏(注意别与覆盖混淆) 21.简述多态实现的原理 编译器发现一个类中有虚函数,便会立即为此类生成虚函数表vtable。 虚函数表的各表项为指向对应虚函数的指针。 编译器还会在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)指向虚函数表。 调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable,将类与此类的vtable联系了起来。 另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,。 如此才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。 22.inline 使用内联函数应注意的事项 内联函数具有一般函数的特性,它与一般函数所不同之处只在于函数调用的处理。 一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。 在使用内联函数时,应注意如下几点: 1.在内联函数内不允许用循环语句和开关语句。 如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码,递归函数(自己调用自己的函数)是不能被用来做内联函数的。 内联函数只适合于只有1~5行的小函数。 对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。 2.内联函数的定义必须出现在内联函数第一次被调用之前。 3.本栏目讲到的类结构中所有在类说明内部定义的函数是内联函数。 23.Const成员函数 const成员函数可以被相同参数表的非const成员函数重载。 在这种情况下,类对象的常量性决定了调用哪个函数。 构造函数和析构函数是两个例外,即使构造函数和析构函数不是const成员函数,const对象也可以调用它们。 23.几个实例 问题(16): 运行如下的C++代码,输出是什么? classA { public: virtualvoidFun(intnumber=10) { std: : cout<<"A: : Funwithnumber"< } }; classB: publicA { public: virtualvoidFun(intnumber=20) { std: : cout<<"B: : Funwithnumber"< } }; intmain() { Bb; A&a=b; a.Fun(); } 答案: 输出B: : Funwithnumber10。 由于a是一个指向B实例的引用,因此在运行的时候会调用B: : Fun。 但缺省参数是在编译期决定的。 在编译的时候,编译器只知道a是一个类型a的引用,具体指向什么类型在编译期是不能确定的,因此会按照A: : Fun的声明把缺省参数number设为10。 这一题的关键在于理解确定缺省参数的值是在编译的时候,但确定引用、指针的虚函数调用哪个类型的函数是在运行的时候。 问题(17): 运行如下的C代码,输出是什么? char*GetString1() { charp[]="HelloWorld"; returnp; } char*GetString2() { char*p="HelloWorld"; returnp; } int_tmain(intargc,_TCHAR*argv[]) { printf("GetString1returns: %s.\n",GetString1()); printf("GetString2returns: %s.\n",GetString2()); return0; } 答案: 输出两行,第一行GetString1returns: 后面跟的是一串随机的内容,而第二行GetString2returns: HelloWorld.两个函数的区别在于GetString1中是一个数组,而GetString2中是一个指针。 当运行到GetString1时,p是一个数组,会开辟一块内存,并拷贝"HelloWorld"初始化该数组。 接着返回数组的首地址并退出该函数。 由于p是GetString1内的一个局部变量,当运行到这个函数外面的时候,这个数组的内存会被释放掉。 因此在_tmain函数里再去访问这个数组的内容时,结果是随机的。 当运行到GetString2时,p是一个指针,它指向的是字符串常量区的一个常量字符串。 该常量字符串是一个全局的,并不会因为退出函数GetString2而被释放掉。 因此在_tmain中仍然根据GetString2返回的地址得到字符串"HelloWorld"。 问题(19): 运行下图中C代码,输出的结果是什么? int_tmain(intargc,_TCHAR*argv[]) { charstr1[]="helloworld"; charstr2[]="helloworld"; char*str3="helloworld"; char*str4="helloworld"; if(str1==str2) printf("str1andstr2aresame.\n"); else printf("str1andstr2arenotsame.\n"); if(str3==str4) printf("str3andstr4aresame.\n"); else printf("str3andstr4arenotsame.\n"); return0; } 答案: 输出两行。 第一行是str1andstr2arenotsame,第二行是str3andstr4aresame。 str1和str2是两个字符串数组。 我们会为它们分配两个长度为12个字节的空间,并把"helloworld"的内容分别拷贝到数组中去。 这是两个初始地址不同的数组,因此比较str1和str2的值,会不相同。 str3和str4是两个指针,我们无需为它们分配内存以存储字符串的内容,而只需要把它们指向"helloworld“在内存中的地址就可以了。 由于"helloworld”是常量字符串,它在内存中只有一个拷贝,因此str3和str4指向的是同一个地址。 因此比较str3和str4的值,会是相同的。 题目(13): 编译运行下图中的C++代码,结果是什么? (A)编译错误;(B)编译成功,运行时程序崩溃;(C)编译运行正常,输出10。 请选择正确答案并分析原因。 #include classA { private: intvalue; public: A(intn) { value=n; } A(Aother) { value=other.value; } voidPrint() { std: : cout< : endl; } }; int_tmain(intargc,_TCHAR*argv[]) { Aa=10; Ab=a; b.Print(); return0; } 答案: 编译错误。 在复制构造函数中传入的参数是A的一个实例。 由于是传值,把形参拷贝到实参会调用复制构造函数。 因此如果允许复制构造函数传值,那么会形成永无休止的递归并造成栈溢出。 因此C++的标准不允许复制构造函数传值参数,而必须是传引用或者常量引用。 在VisualStudio和GCC中,都将编译出错。 题目(七): 运行下列C++代码,输出什么? classA { public: A() { Print(); } virtualvoidPrint() { printf("Aisconstructed.\n"); } }; classB: publicA { public: B() { Print(); } virtualvoidPrint() { printf("Bisconstructed.\n"); } }; int_tmain(intargc,_TCHAR*argv[]) { A*pA=newB(); deletepA; return0; } 答案: 先后打印出两行: Aisconstructed.Bisconstructed.调用B的构造函数时,先会调用B的基类及A的构造函数。 然后在A的构造函数里调用Print。 由于此时实例的类型B的部分还没有构造好,本质上它只是A的一个实例,他的虚函数表指针指向的是类型A的虚函数表。 因此此时调用的Print是A: : Print,而不是B: : Print。 接着调用类型B的构造函数,并调用Print。 此时已经开始构造B,因此此时调用的Print是B: : Print。 同样是调用虚拟函数Print,我们发现在类型A的构造函数中,调用的是A: : Print,在B的构造函数中,调用的是B: : Print。 因此虚函数在构造函数中,已经失去了虚函数的动态绑定特性。 题目 (一): 我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量)。 请问: 能不能同时用static和const修饰类的成员函数? 分析: 答案是不可以。 C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数constthis*。 但当一个成员为static的时候,该函数是没有this指针的。 也就是说此时static的用法和const是冲突的。 我们也可以这样理解: 两者的语意是矛盾的。 static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。 因此不能同时用它们。 题目 (二): 运行下面的代码,输出是什么? classA { }; classB { public: B(){} ~B(){} }; classC { public: C(){} virtual~C(){} }; int_tmain(intargc,_TCHAR*argv[]) { printf("%d,%d,%d\n",sizeof(A),
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 程序员 试题 整理 算法
![提示](https://static.bdocx.com/images/bang_tan.gif)