东软CC++笔试Word文件下载.docx
《东软CC++笔试Word文件下载.docx》由会员分享,可在线阅读,更多相关《东软CC++笔试Word文件下载.docx(18页珍藏版)》请在冰豆网上搜索。
*strDest目的字符串,*strSrc源字符串,length源字符串的长度
函数实现为:
char*convert(char*strDest,constchar*strSrc,intlength)
char*cp=strDest;
inti=0;
while(*strSrc&
&
i{
if(*strSrc==’\t’)//将\t转换成4个空格
for(intj=0;
j<
4;
j++)
*cp++='
'
;
else//否则直接拷贝
*cp++=*strSrc;
strSrc++;
i++;
returnstrDest;
7.下列哪两个是等同的
intb;
Aconstint*a=&
b;
Bconst*inta=&
Cconstint*consta=&
Dintconst*consta=&
各式表示的意思分别为:
//*a是const,但指针a可变
//a是const,但*a可变
//a和*a都是const,常量和指针的值都不能改变
因此C,D两者是相同的。
总结个技巧:
如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
5.struct和class有什么区别?
默认的访问级别不同,struct是public,class是private
6.没有别的不同了吗?
再看看下面的一段程序有什么错误:
Code
swap(int*p1,int*p2)
int*p;
*p=*p1;
*p1=*p2;
*p2=*p;
在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。
在VC++中DEBUG运行时提示错误“AccessViolation”。
野指针,也就是指向不可用内存区域的指针。
通常对这种指针进行操作的话,将会使程序发生不可预知的错误。
“野指针”不是NULL指针,是指向“垃圾”内存的指针。
人们一般不会错用NULL指针,因为用if语句很容易判断。
但是“野指针”是很危险的,if语句对它不起作用。
野指针的成因主要有两种:
一、指针变量没有被初始化。
任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。
所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。
通常会用语句if(p!
=NULL)进行防错处理。
很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。
例:
char*p=(char*)malloc(100);
strcpy(p,“hello”);
//p所指的内存被释放,但是p所指的地址仍然不变
if(p!
=NULL)//没有起到防错作用
strcpy(p,“world”);
//出错
另外一个要注意的问题:
不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
该程序应该改为:
intp;
p=*p1;
*p2=p;
3.内功题
试题1:
分别给出BOOL,int,float,指针变量与“零值”比较的if语句(假设变量名为var)
解答:
BOOL型变量:
var)
int型变量:
if(var==0)
float型变量:
constfloatEPSINON=0.00001;
if((x>
=–EPSINON)&
(x<
=EPSINON)
指针变量:
if(var==NULL)
剖析:
考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!
var),指针变量的判断也可以写成if(!
var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!
var),表明其为“逻辑”判断;
如果用if判断一个数值型变量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;
而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。
浮点型变量并不精确,所以不可将float变量用“==”或“!
=”与数字比较,应该设法转化成“>
=”或“<
=”形式。
如果写成if(x==0.0),则判为错,得0分。
试题2:
以下为WindowsNT下的32位C++程序,请计算sizeof的值
voidFunc(charstr[100])
sizeof(str)=?
void*p=malloc(100);
sizeof(p)=?
sizeof(str)=4
sizeof(p)=4
Func(charstr[100])函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;
在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
数组名的本质如下:
(1)数组名指代一种数据结构,这种数据结构就是数组;
例如:
charstr[10];
cout<
<
sizeof(str)<
endl;
输出结果为10,str指代数据结构char[10]。
(2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
str++;
//编译出错,提示str不是左值
(3)数组名作为函数形参时,沦为普通指针。
WindowsNT32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof(str)、sizeof(p)都为4。
试题3:
写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
另外,当你写下面的代码时会发生什么事?
least=MIN(*p++,b);
#defineMIN(A,B)((A)<
=(B)?
(A):
(B))
MIN(*p++,b)会产生宏的副作用
这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。
程序员对宏定义的使用要非常小心,特别要注意两个问题:
(1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。
所以,严格地讲,下述解答:
#defineMIN(A,B)(A)<
(B)
#defineMIN(A,B)(A<
=B?
A:
B)都应判0分;
(2)防止宏的副作用。
宏定义#defineMIN(A,B)((A)<
(B))对MIN(*p++,b)的作用结果是:
((*p++)<
=(b)?
(*p++):
(*p++))这个表达式会产生副作用,指针p会作三次++自增操作。
除此之外,另一个应该判0分的解答是:
(B));
这个解答在宏定义的后面加“;
”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。
宏的一些副作用
1、优先级问题
1)传入变量优先级
#defineMULTI(a,b)a*b
MULTI(1+2,3)=>
1+2*3其实是想要(1+2)*3
2)作为值返回时,类似1)
#defineADD(a,b)(a)+(b)
intc=ADD(a,b)*3;
=>
(a)+(b)*3其实是想要(a+b)*3
所以,一般的规则是:
宏里面参数全部用括号括起来;
如果作为值返回,整个表达式也用括号括起来。
所以,上面最好这么写:
#defineMULTI(a,b)((a)*(b))
#defineADD(a,b)((a)+(b))
2、实际使用参数和宏内部变量同名
#defineHASH(str,sz,rst)do{unsignedintn=0;
n=xxx;
rst=n%sz;
}while(0)
这是一个hash的宏实现,其中定义了一个临时变量n,根据str计算n,然后对sz求模并把返回值赋给传进来的rst.
这么调用:
intn;
HASH(“hello”,7,n);
不会达到改变n的效果,因为实际使用参数n和宏内部的变量n同名。
宏扩展中最后一条语句是:
n=n%sz;
因为宏内部n有更小作用域,实际赋值的是宏内部的那个临时变量n。
外面调用的n不会有任何改变。
这个副作用有些隐蔽,一般的规则是:
宏内部变量使用一种不同风格的命名方式。
比如:
#defineHASH(str,sz,rst)do{unsignedint__n=0;
__n=…
3、++,–
#defineMAX(a,b)((a)>
(b)?
(a):
(b))
inta=3,b=2;
intc=MAX(a++,b);
执行看看,不但a的值不是和想要的一致,返回值c也会让你大吃一惊,哈哈。
(a=5,c=4)
在宏内部一个变量”执行”多少次,它就自增或自减了多少次。
所以一般使用宏最好不要传入自增自减。
如果你一定要在宏里消除这个副作用,可以这样:
#defineMAX(a,b)({int__x=(a),__y=(b);
(__x>
__y)?
__x:
__y;
})
也就是:
保证传入宏的参数在内部只使用一次。
(注意:
传入a++或++a都能得到各自正确的效果)
这里的内部变量__x,__y是不需要用括号包起来的,原因可以自己想想。
另外对宏中括号的使用补充说明两点:
因为宏中定义了临时变量,所以要用{}括起来;
因为要返回值,所以外面还要用()括起来({}不返回值);
另外,这里还有一个问题:
实际中a,b不一定是int的,这个宏中的临时变量声明为int,不通用。
改进:
#defineMAX(a,b,type)({type__x=(a),__y=(b);
使用:
MAX(1,2,int);
MAX(1.1,1.2,double);
是不是感觉怪怪的,有点c++的感觉~~这样的使用太复杂了,而且也会给代码的阅读带来难度。
我觉得好的态度是多了解些宏的可能的副作用,在实际编码中遵守第1、2条规则,不要往宏中传入自增自减的东西,就够了。
不要把过多的复杂度全扔给宏,”通用”也不能盲目,因为毕竟:
yy是没有极限的。
试题4:
为什么标准头文件都有类似以下的结构?
#ifndef__INCvxWorksh
#define__INCvxWorksh
#ifdef__cplusplus
extern“C”{
#endif
/**/
#endif/*__INCvxWorksh*/
头文件中的编译宏
#ifndef __INCvxWorksh
#define __INCvxWorksh
的作用是防止被重复引用。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。
函数被C++编译后在symbol库中的名字与C语言的不同。
例如,假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。
为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern“C”来解决名字匹配问题,函数声明前加上extern“C”后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
试题5:
编写一个函数,作用是把一个char组成的字符串循环右移n个。
比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh”
函数头是这样的:
//pStr是指向以’\0′结尾的字符串的指针
//steps是要求移动的n
voidLoopMove(char*pStr,intsteps)
//请填充…
正确解答1:
voidLoopMove(char*pStr,intsteps)
intn=strlen(pStr)–steps;
chartmp[MAX_LEN];
strcpy(tmp,pStr+n);
strcpy(tmp+steps,pStr);
*(tmp+strlen(pStr))=‘\0′;
strcpy(pStr,tmp);
正确解答2:
memcpy(tmp,pStr+n,steps);
memcpy(pStr+steps,pStr,n);
memcpy(pStr,tmp,steps);
这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。
最频繁被使用的库函数包括:
(1)strcpy
(2)memcpy
(3)memset
memcpy
原型:
externvoid*memcpy(void*dest,void*src,unsignedintcount);
用法:
#include
功能:
由src所指内存区域复制count个字节到dest所指内存区域。
说明:
src和dest所指内存区域不能重叠,函数返回指向dest的指针。
注意:
与strcpy相比,memcpy并不是遇到’\0′就结束,而是一定会拷贝完n个字节。
举例:
//memcpy.c
#include
intmain(intargc,char*argv[])
{
char*s=”GoldenGlobalView”;
chard[20];
clrscr();
memcpy(d,s,strlen(s));
d[strlen(s)]=’\0′;
printf(“%s”,d);
getchar();
return0;
}
截取view
memcpy(d,s+14,4);
//memcpy(d,s+14*sizeof(char),4*sizeof(char));
也可
d[4]=’\0′;
输出结果:
View
初始化数组
charmsg[10];
memcpy(msg,0,sizeof(msg));
memset
函数原型
void*memset(void*s,intch,unsignedn);
编辑本段
程序例
memset函数
intmain(void)
charbuffer[]=“Helloworld\n”;
printf(“Bufferbeforememset:
%s\n”,buffer);
memset(buffer,‘*’,strlen(buffer));
printf(“Bufferaftermemset:
Bufferbeforememset:
Helloworld
Bufferaftermemset:
***********
编译平台:
MicrosoftVisualC++6.0
也不一定就是把内容全部设置为ch指定的ASCII值,而且该处的ch可为int或者其他类型,并不一定要是char类型。
例如下面这样:
intarray[5]={1,4,3,5,2};
for(inti=0;
i<
5;
i++)
cout<
"
?
memset(s,'
G'
6);
//貌似这里有点问题//单步运行到这里会提示内存访问冲突
printf("
%s"
s);
}
【应该是没有问题的,字符串指针一样可以,并不是只读内存,可以正常运行】
3。
memset()函数常用于内存空间初始化。
如:
charstr[100];
memset(str,0,100);
4。
memset()的深刻内涵:
用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘memset(a,'
\0'
sizeof(a));
memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;
chara[100],b[50];
memcpy(b,a,sizeof(b));
//注意如用sizeof(a),会造成b的内存地址溢出。
strcpy就只能拷贝字符串了,它遇到'
就结束拷贝;
strcpy(a,b);
如用strcpy(b,a),要注意a中的字符串长度(第一个‘\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。
5.补充:
某人的一点心得
memset可以方便的清空一个结构类型的变量或数组。
如:
structsample_struct
charcsName[16];
intiSeq;
intiType;
};
对于变量
structsample_strcutstTest;
一般情况下,清空stTest的方法:
stTest.csName[0]='
stTest.iSeq=0;
stTest.iType=0;
用memset就非常方便:
memset(&
stTest,0,sizeof(structsample_struct));
如果是数组:
structsample_structTEST[10];
则
memset(TEST,0,sizeof(structsample_struct)*10);
试题8:
请说出static和const关键字尽可能多的作用
static关键字至少有下列n个作用:
(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const关键字至少有下列n个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。
在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时