程序设计面试题.docx

上传人:b****7 文档编号:9604081 上传时间:2023-02-05 格式:DOCX 页数:56 大小:43.60KB
下载 相关 举报
程序设计面试题.docx_第1页
第1页 / 共56页
程序设计面试题.docx_第2页
第2页 / 共56页
程序设计面试题.docx_第3页
第3页 / 共56页
程序设计面试题.docx_第4页
第4页 / 共56页
程序设计面试题.docx_第5页
第5页 / 共56页
点击查看更多>>
下载资源
资源描述

程序设计面试题.docx

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

程序设计面试题.docx

程序设计面试题

一、volatile

volatile影响编译器的编译结果。

volatile变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。

)。

例如:

volatileinti=10;

intj=i;

...

intk=i;

volatile告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。

优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。

而不是重新从i里面读。

这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

精确地说,就是优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

下面是volatile变量的几个例子:

1)并行设备的硬件寄存器(如:

状态寄存器)

2)一个中断服务子程序中会访问到的非自动变量(Non-automaticvariables)

3)多线程应用中被几个任务共享的变量

1)一个参数既可以是const还可以是volatile吗?

解释为什么。

2)一个指针可以是volatile吗?

解释为什么。

Constchar*p

3)下面的函数有什么错误:

intsquare(volatileint*ptr)

{

return*ptr**ptr;

}

下面是答案:

1)是的。

一个例子是只读的状态寄存器。

它是volatile的,因为它可能被意想不到地改变。

它是const的,因为程序不应该试图去修改它。

2)是的。

尽管这并不很常见。

一个例子是:

当一个中服务子程序修该一个指向一个buffer的指针时。

3)这段代码有点变态。

这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

intsquare(volatileint*ptr)

{

inta,b;

a=*ptr;b=*ptr;returna*b;

}

由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。

结果,这段代码可能返不回你所期望的平方值!

正确的代码如下:

longsquare(volatileint*ptr)

{

inta;

a=*ptr;

returna*a;

}

嵌入式编程中经常用到volatile这个关键字,在网上查了下他的用法可以归结为以下两点:

1.告诉compiler不能做任何优化

比如要往某一地址送两指令:

int*ip=...;//设备地址

*ip=1;//第一个指令

*ip=2;//第二个指令

以上程序compiler可能做优化而成:

int*ip=...;

*ip=2;

结果第一个指令丢失。

如果用volatile,compiler就不允许做任何的优化,从而保证程序的原意:

volatileint*ip=...;

*ip=1;

*ip=2;

即使你要compiler做优化,它也不会把两次赋值语句简化为一。

它只能做其它的优化。

这对devicedriver程序员很有用。

2.表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。

如volatilechara;

a=0;

while(!

a){

//dosomethings;

}

do_otherthing();

如果没有volatile,do_otherthing()不会被执行

二、声明和定义

从更本质的角度来说,声明与定义的区别在于前者完全不需要分配存储空间而后者需要。

但是声明与定义的形式丰富,必须了解什么样形式的cpp句子是声明句、又是什么样的句子才能叫做定义句。

对于内置类型变量来说:

(1)所有以内置类型名开头+空格+变量名;的句子都是定义句。

如intx;charc;

(2)所有extern+空格+内置类型名+空格+变量名;形式的句子都是声明句。

如externintx;externcharc;

(3)所有extern+空格+内置类型名+空格+变量名=常量;形式的句子都是定义句。

如externintx=1;externcharc='k';

注意,第2与第3形式在的差别只在于有无赋值行为,而正是因为这点差别,才导致了第二条中提到的语句属于声明句、第三条中的语句属于定义句。

因为从本质上看,有赋值行为就肯定要分配存储空间,而分配存储空间就说明这个是一个定义的行为了。

三、C++面试题

问题1.是不是一个父类写了一个virtual函数,如果子类覆盖它的函数不加virtual,也能实现多态?

virtual修饰符会被隐形继承的。

private也被继承,只是派生类没有访问权限而已

virtual可加可不加

子类的空间里有父类的所有变量(static除外)

同一个函数只存在一个实体(inline除外)

子类覆盖它的函数不加virtual,也能实现多态。

在子类的空间里,有父类的私有变量。

私有变量不能直接访问。

问题2.输入一个字符串,将其逆序后输出。

(使用C++,不建议用伪码)

#include

usingnamespacestd;

voidmain()

{

chara[50];

memset(a,0,sizeof(a));

inti=0,j;

chart;

cin.getline(a,50,'\n');

for(i=0,j=strlen(a)-1;i

{

t=a[i];a[i]=a[j];a[j]=t;

}

cout<

}

//第二种:

string的replace根本不能实现这个功能

stringstr;

cin>>str;

str.replace;

cout<

我的代码是这样的:

stringa="12345";

copy(a.rbegin(),a.rend(),ostream_iterator(cout,""));

或者:

stringa="12345";

reverse(a.rbegin(),a.rend());

cout<

问题3.请简单描述Windows内存管理的方法

内存管理是操作系统的重要部分,先说个大概,希望能够抛砖引玉

程序运行时需要从内存中读出这段程序的代码:

代码的位置必须在物理内存中才能被运行;t同时有多个程序在运行,内存中不够用,所以引出虚拟内存的概念,把那些不常用的程序片断就放入虚拟内存,当需要用它的时候再load入主存(物理内存)中。

这就是内存管理所要做的事。

内存管理还有另外一件事需要做:

计算出程序片段在主存中的物理位置,以便CPU调度。

内存管理有块式管理,页式管理,段式和段页式管理。

现在常用段页式管理。

块式管理:

把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。

这样会造成很大的浪费,平均浪费了50%的内存空间,但时易于管理。

页式管理:

把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。

段式管理:

把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。

一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。

段页式管理:

结合了段式管理和页式管理的优点。

把主存分为若干页,每一页又分为若干段。

好处就很明显,不用我多说了吧。

各种内存管理都有它自己的方法来计算出程序片断在主存中的物理地址,其实都很相似。

问题4.阅读程序

#include"stdafx.h"

#defineSQR(X)X*X

intmain(intargc,char*argv[])

{

inta=10;

intk=2;

intm=1;

a/=SQR(k+m)/SQR(k+m);

printf("%d\n",a);

return0;

}

define只是定义而已,在编择时只是简单代换X*X而已,并不经过算术法则的

a/=k+m*k+m/k+m*k+m;

a/=2+1*2+1/2+1*2+1

=>a=a/7;

4作者的讲解是正确的,只是例子的解答不正确,但是从错误的推论得到了正确的结果,我仅给出反汇编的代码。

inta=10;

intk=2;

intm=1;

__asm{

moveax,m

imuleax,k

movecx,k

addecx,eax

moveax,m

cdq

idiveax,k

addeax,m

movesi,m

imulesi,k

addeax,ecx

addesi,eax

moveax,a

cdq

idiveax,esi

mova,eax

}

问题5.const符号常量

(1)constchar*p

(2)charconst*p

(3)char*constp

说明上面三种描述的区别:

如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

(1)constchar*p

一个指向char类型的const对象指针,p不是常量,我们可以修改p的值,使其指向不同的char,但是不能改变它指向非char对象,如:

constchar*p;

charc1='a';

charc2='b';

p=&c1;//ok

p=&c2;//ok

*p=c1;//error

(2)charconst*p

(3)char*constp

这两个好象是一样的,此时*p可以修改,而p不能修改

(4)constchar*constp

这种是地址及指向对象都不能修改。

5其实只是对const的考察,const是常量,单词constant的缩写。

所以

constchar*p;和charconst*p;的作用是相同的,说明这个指针可以变化,但是指针指向的内容不能变化。

char*constp;说明指针是不可以变化的,但是其能容可以变化

问题6.下面是C语言中两种if语句判断方式。

请问哪种写法更好?

为什么?

intn;

if(n==10)//第一种判断方式

if(10==n)//第二种判断方式

如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了=

问题7.下面的代码有什么问题?

voidDoSomeThing(...)

{

char*p;

...

p=malloc(1024);//分配1K的空间

if(NULL==p)

return;

...

p=realloc(p,2048);//空间不够,重新分配到2K

if(NULL==p)

return;

...

}

A:

p=malloc(1024);应该写成:

p=(char*)malloc(1024);

没有释放p的空间,造成内存泄漏

还有一个问题,如下写程序才能保证没错

voidDoSomeThing(...)

{

char*p,*q;

...

p=malloc(1024);//分配1K的空间

if(NULL==p)

return;

...

<<<<<<<<<<<<<<<<<<<<

q=p;

>>>>>>>>>>>>>>>>>>>>>

p=realloc(p,2048);//空间不够,重新分配到2K

if(NULL==p){

free(q);

return;

}

问题8.下面的代码有什么问题?

请给出正确的写法。

voidDoSomeThing(char*p)

{

charstr[16];

intn;

assert(NULL!

=p);

sscanf(p,"%s%d",str,n);

if(0==strcmp(str,"something"))

{...

}

}

A:

sscanf(p,"%s%d",str,n);这句该写成:

sscanf(p,"%s%d",str,&n);

问题9.下面的代码有什么问题?

voidtest1()

{

charstring[10];

char*str1="0123456789";

strcpy(string,str1);

}

数组越界

问题10.下面的代码有什么问题?

voidtest2()

{

charstring[10],str1[10];

for(i=0;i<10;i++)

{

str1[i]='a';

}

strcpy(string,str1);

}

问题11.下面的代码有什么问题?

voidtest3(char*str1)

{

charstring[10];

if(strlen(str1)<=10)

{

strcpy(string,str1);

}

}

==数组越界

==strcpy拷贝的结束标志是查找字符串中的\0因此如果字符串中没有遇到\0的话会一直复制,直到遇到\0,上面的123都因此产生越界的情况

建议使用strncpy和memcpy

问题12.下面的代码有什么问题?

#defineMAX_SRM256

DSNget_SRM_no()

{

staticintSRM_no;//是不是这里没赋初值?

intI;

for(I=0;I

{

SRM_no%=MAX_SRM;

if(MY_SRM.state==IDLE)

{

break;

}

}

if(I>=MAX_SRM)

return(NULL_SRM);

else

returnSRM_no;

}

系统会初始化staticint变量为0,但该值会一直保存,所谓的不可重入

for(I=0;I

for(I=SRM_no;I

问题13.写出运行结果

{//test1

charstr[]="world";cout<

";

char*p=str;cout<

";

chari=10;cout<

";

void*pp=malloc(10);cout<

}

6:

4:

1:

4

问题14.写出运行结果

{//test2

unionV{

structX{

unsignedchars1:

2;

unsignedchars2:

3;

unsignedchars3:

3;

}x

unsignedcharc;

}v;

v.c=100;

printf("%d",v.x.s3);

}

3

问题15.用C++写个程序,如何判断一个操作系统是16位还是32位的?

不能用sizeof()函数

A1:

16位系统下,

inti=65536;

cout<

inti=65535;

cout<

32位的系统下,

inti=65536;

cout<

inti=65535;

cout<

A2:

inta=~0;

if(a>65536)

{

cout<<"32bit"<

}

else

{

cout<<"16bit"<

}

问题16.C和C++有什么不同?

机制:

c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。

但是,c++编写面向对象的程序比c容易

适用方向:

c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的;linux核心大部分是c写的,因为它是系统软件,效率要求极高。

从名称上也可以看出,c++比c多了+,说明c++是c的超集;那为什么不叫c+而叫c++呢,是因为c++比c来说扩充的东西太多了,所以就在c后面放上两个+;于是就成了c++

C语言是结构化编程语言,C++是面向对象编程语言。

C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。

问题17.在不用第三方参数的情况下,交换两个参数的值

#include

voidmain()

{

inti=60;

intj=50;

i=i+j;

j=i-j;

i=i-j;

printf("i=%d\n",i);

printf("j=%d\n",j);

}

方法二:

i^=j;

j^=i;

i^=j;

方法三:

//用加减实现,而且不会溢出

a=a+b-(b=a)

问题18.有关位域的面试题(为什么输出的是一个奇怪的字符)

a.t='b';效果相当于a.t='b'&0xf;

'b'-->01100010

'b'&0xf-->>00000010

所以输出Ascii码为2的特殊字符

chart:

4;就是4bit的字符变量,同样

unsignedshorti:

8;就是8bit的无符号短整形变量

问题19.inti=10,j=10,k=3;k*=i+j;k最后的值是?

60

问题20.进程间通信的方式有?

进程间通信的方式有共享内存,管道,Socket,消息队列,DDE等

问题21.进程间通信的方式有?

structA

{

chart:

4;

chark:

4;

unsignedshorti:

8;

unsignedlongm;

}

sizeof(A)=?

(不考虑边界对齐)7

structCELL//DeclareCELLbitfield

{

unsignedcharacter:

8;//00000000?

?

?

?

?

?

?

?

unsignedforeground:

3;//00000?

?

?

00000000

unsignedintensity:

1;//0000?

00000000000

unsignedbackground:

3;//0?

?

?

000000000000

unsignedblink:

1;//?

000000000000000

}screen[25][80];//Arrayofbitfields

二、位结构

位结构是一种特殊的结构,在需按位访问一个字节或字的多个位时,位结构

比按位运算符更加方便。

位结构定义的一般形式为:

struct位结构名{

数据类型变量名:

整型常数;

数据类型变量名:

整型常数;

}位结构变量;

其中:

数据类型必须是int(unsigned或signed)。

整型常数必须是非负的整

数,范围是0~15,表示二进制位的个数,即表示有多少位。

变量名是选择项,可以不命名,这样规定是为了排列需要。

例如:

下面定义了一个位结构。

struct{

unsignedincon:

8;/*incon占用低字节的0~7共8位*/

unsignedtxcolor:

4;/*txcolor占用高字节的0~3位共4位*/

unsignedbgcolor:

3;/*bgcolor占用高字节的4~6位共3位*/

unsignedblink:

1;/*blink占用高字节的第7位*/

}ch;

位结构成员的访问与结构成员的访问相同。

例如:

访问上例位结构中的bgcolor成员可写成:

ch.bgcolor

注意:

1.位结构中的成员可以定义为unsigned,也可定义为signed,但当成员长

度为1时,会被认为是unsigned类型。

因为单个位不可能具有符号。

2.位结构中的成员不能使用数组和指针,但位结构变量可以是数组和指针,

如果是指针,其成员访问方式同结构指针。

3.位结构总长度(位数),是各个位成员定义的位数之和,可以超过两个字

节。

4.位结构成员可以与其它结构成员一起使用。

例如:

structinfo{

charname[8];

intage;

structaddraddress;

floatpay;

unsignedstate:

1;

unsignedpay:

1;

}workers;

上例的结构定义了关于一个工人的信息。

其中有两个位结构成员,每个位结构成员只有一位,因此只占一个字节但保存了两个信息,该字节中第一位表示工

人的状态,第二位表示工资是否已发放。

由此可见使用位结构可以节省存贮空间。

问题22.下面的函数实现在一个固定的数上加上一个数,有什么错误,改正

intadd_n(intn)

{

staticinti=100;

i+=n;

returni;

}

答:

因为static使得i的值会保留上次的值。

去掉static就可

问题23.下面的代码有什么问题?

classA

{

public:

A(){p=this;}

~A(){if(p!

=

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

当前位置:首页 > 外语学习 > 韩语学习

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

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