程序员面试题整理非算法题.docx

上传人:b****5 文档编号:3566265 上传时间:2022-11-23 格式:DOCX 页数:79 大小:873.27KB
下载 相关 举报
程序员面试题整理非算法题.docx_第1页
第1页 / 共79页
程序员面试题整理非算法题.docx_第2页
第2页 / 共79页
程序员面试题整理非算法题.docx_第3页
第3页 / 共79页
程序员面试题整理非算法题.docx_第4页
第4页 / 共79页
程序员面试题整理非算法题.docx_第5页
第5页 / 共79页
点击查看更多>>
下载资源
资源描述

程序员面试题整理非算法题.docx

《程序员面试题整理非算法题.docx》由会员分享,可在线阅读,更多相关《程序员面试题整理非算法题.docx(79页珍藏版)》请在冰豆网上搜索。

程序员面试题整理非算法题.docx

程序员面试题整理非算法题

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)

想法:

实现一个和一般类除了不能被继承之外其他用法都一样的类

templateclassMakeFinal

{

     friendT;

private:

     MakeFinal(){}

     ~MakeFinal(){}

};

 

classFinalClass2:

virtualpublicMakeFinal

{

public:

     FinalClass2(){}

     ~FinalClass2(){}

};

此时FinalClass2就不能再被继承了,由于类FinalClass2是从类MakeFinal虚继承过来的,在调用Try的构造函数的时候,会直接跳过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),

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 小升初

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1