经典CC++笔试题.docx
- 文档编号:8257098
- 上传时间:2023-01-30
- 格式:DOCX
- 页数:35
- 大小:45.30KB
经典CC++笔试题.docx
《经典CC++笔试题.docx》由会员分享,可在线阅读,更多相关《经典CC++笔试题.docx(35页珍藏版)》请在冰豆网上搜索。
经典CC++笔试题
1.编写一个标准strcpy函数
char * strcpy( char *strDest, const char *strSrc )
{
//将源字符串加const,表明其为输入参数
//对源地址和目的地址加非0断言
assert( (strDest !
= NULL) && (strSrc !
= NULL) );
char *address = strDest;
while( (*strDest++ = * strSrc++) !
= ‘\0’ );
return address;
}
strcpy能把strSrc的内容复制到strDest,为什么还要char*类型的返回值?
答:
为了实现链式表达式。
//2分
例如intlength=strlen(strcpy(strDest,“helloworld”));
2.对strlen的掌握,它没有包括字符串末尾的'\0'。
strlen函数了,完美的版本为:
int strlen( const char *str ) //输入参数const
{
assert( strt !
= NULL ); //断言字符串地址非0
int len=0;
while( (*str++) !
= '\0' )
{
len++;
}
return len;
}
3.分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)
解答:
BOOL型变量:
if(!
var)
int型变量:
if(var==0)
float型变量:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指针变量:
if(var==NULL)
浮点型变量并不精确,所以不可将float变量用“==”或“!
=”与数字比较,应该设法转化成“>=”或“<=”形式。
4.数组名的本质如下:
(1)数组名指代一种数据结构,这种数据结构就是数组;
例如:
char str[10];
cout << sizeof(str) << endl;
输出结果为10,str指代数据结构char[10]。
(2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
char str[10];
str++; //编译出错,提示str不是左值
(3)数组名作为函数形参时,沦为普通指针。
5.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
另外,当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
解答:
#define MIN(A,B) ((A) <= (B) ?
(A) :
(B))
MIN(*p++, b)会产生宏的副作用 指针P会做3次++自增操作
6.编写一个函数,作用是把一个char组成的字符串循环右移n个.比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的:
//pStr是指向以'\0'结尾的字符串的指针
//steps是要求移动的n
void LoopMove ( char * pStr, int steps )
{
//请填充...
}
解答:
正确解答1:
void LoopMove ( char *pStr, int steps )
{
int n = strlen( pStr ) - steps;
char tmp[MAX_LEN];
strcpy ( tmp, pStr + n );
strcpy ( tmp + steps, pStr);
*( tmp + strlen ( pStr ) ) = '\0';
strcpy( pStr, tmp );
}
正确解答2:
void LoopMove ( char *pStr, int steps )
{
int n = strlen( pStr ) - steps;
char tmp[MAX_LEN];
memcpy( tmp, pStr + n, steps );
memcpy(pStr + steps, pStr, n );
memcpy(pStr, tmp, steps );
}
7.已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。
WAVE文件格式说明表
偏移地址 字节数 数据类型 内 容
文件头
00H 4 Char "RIFF"标志
04H 4 int32 文件长度
08H 4 Char "WAVE"标志
0CH 4 Char "fmt"标志
10H 4 过渡字节(不定)
14H 2 int16 格式类别
16H 2 int16 通道数
18H 2 int16 采样率(每秒样本数),表示每个通道的播放速度
1CH 4 int32 波形音频数据传送速率
20H 2 int16 数据块的调整数(按字节算的)
22H 2 每样本的数据位数
24H 4 Char 数据标记符”data”
28H 4 int32 语音数据的长度
解答:
将WAV文件格式定义为结构体WAVEFORMAT:
typedef struct tagWaveFormat
{
char cRiffFlag[4];
UIN32 nFileLen;
char cWaveFlag[4];
char cFmtFlag[4];
char cTransition[4];
UIN16 nFormatTag ;
UIN16 nChannels;
UIN16 nSamplesPerSec;
UIN32 nAvgBytesperSec;
UIN16 nBlockAlign;
UIN16 nBitNumPerSample;
char cDataFlag[4];
UIN16 nAudioLength;
} WAVEFORMAT;
假设WAV文件内容读出后存放在指针buffer开始的内存单元内,则分析文件格式的代码很简单,为:
WAVEFORMAT waveFormat;
memcpy( &waveFormat, buffer,sizeof( WAVEFORMAT ) );
直接通过访问waveFormat的成员,就可以获得特定WAV文件的各项格式信息。
8.编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
解答:
//普通构造函数
String:
:
String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 得分点:
对空字符串自动申请存放结束标志'\0'的空
//加分点:
对m_data加NULL 判断
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, str);
}
}
// String的析构函数
String:
:
~String(void)
{
delete [] m_data; // 或delete m_data;
//由于m_data是内部数据类型,也可以写成deletem_data;
}
//拷贝构造函数
String:
:
String(const String &other) // 得分点:
输入参数为const型
{
int length = strlen(other.m_data);
m_data = new char[length+1]; //加分点:
对m_data加NULL 判断
strcpy(m_data, other.m_data);
}
//赋值函数
String & String:
:
operate =(const String &other) // 得分点:
输入参数为const型
{
if(this == &other) //得分点:
检查自赋值
return *this;
delete [] m_data; //得分点:
释放原有的内存资源
int length = strlen( other.m_data );
m_data = new char[length+1]; //加分点:
对m_data加NULL 判断
strcpy( m_data, other.m_data );
return *this; //得分点:
返回本对象的引用
}
剖析:
能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上!
在这个类中包括了指针类成员变量m_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。
9.请说出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,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
例如:
const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个const对象。
如果不是,这样的变态代码也不会编译出错:
classA a, b, c;
(a * b) = c; // 对a*b的结果赋值
操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。
const意味着声明的变量为只读变量,其值不可改变。
constinta;a是一个常整型数
intconsta;a是一个常整型数
constint*a;a是一个指向常整型数的指针
int*consta;a是一个指向整型数的常指针
intconst*consta;a是一个指向常整型数的常指针
const可以修饰函数的参数、返回值,甚至函数的定义体。
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
10.关键字volalile:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,被定义为volatile的变量,优化器每次用到这个变量时都会重新读取这个值,而不会用保存在寄存器中的值,防止变量被优化。
例子:
1)并行设备的硬件寄存器
2)一个中断服务子程序中会访问到的非自动变量(Non-automaticvariables)
3)多线程应用中被几个任务共享的变量
11.用变量a给出下面的定义
a)一个整型数inta;
b)一个指向整型数的指针int*a;
c)一个指向指针的的指针,它指向的指针是指向一个整型数int**a;
d)一个有10个整型数的数组inta[10];
e)一个有10个指针的数组,该指针是指向一个整型数的int*a[10];
f)一个指向有10个整型数数组的指针int(*a)[10];
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数int(*a)(int);
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数int(*a[10])(int)
12.访问一绝对地址把一个整型数强制转换为一指针是合法的:
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa55;
要对绝对地址0x100000赋值,我们可以用
(unsignedint*)0x100000=1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
*((void(*)())0x100000)();
首先要将0x100000强制转换成函数指针,即:
(void(*)())0x100000
然后再调用它:
*((void(*)())0x100000)();
用typedef可以看得更直观些:
typedefvoid(*)()voidFuncPtr;
*((voidFuncPtr)0x100000)();
13.字符数组与字符指针的区别:
字符指针存放在文字常量区,指向地址可变;字符串数组所代表的数组首地址不可变。
14.Change宏定义do{}while(0)
#include
#defineswap(a,b)do{a=a+b;b=a-b;a=a-b;}while(0)
15.在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”?
答:
C++语言支持函数重载,C语言不支持函数重载。
函数被C++编译后在库中的名字与C语言的不同。
假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
详解:
首先,作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。
例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。
这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数
extern"C"是连接申明(linkagedeclaration),被extern"C"修饰的变量和函数是按照C语言方式编译和连接的,来看看C++中对类似。
C的函数是怎样编译的:
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。
函数被C++编译后在符号库中的名字与C语言的不同。
例如,假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。
_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。
例如,在C++中,函数voidfoo(intx,inty)与voidfoo(intx,floaty)编译生成的符号是不相同的,后者为_foo_int_float。
同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。
用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。
而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
未加extern"C"声明时的连接方式
假设在C++中,模块A的头文件如下:
//模块A头文件 moduleA.h
#ifndefMODULE_A_H
#defineMODULE_A_H
intfoo(intx,inty);
#endif
在模块B中引用该函数:
//模块B实现文件 moduleB.cpp
#include"moduleA.h"
foo(2,3);
实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
加extern"C"声明后的编译和连接方式
加extern"C"声明后,模块A的头文件变为:
//模块A头文件 moduleA.h
#ifndefMODULE_A_H
#defineMODULE_A_H
extern"C"intfoo(intx,inty);
#endif
在模块B的实现文件中仍然调用foo(2,3),其结果是:
(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。
如果在模块A中函数声明了foo为extern"C"类型,而模块B中包含的是externintfoo(intx,inty),则模块B找不到模块A中的函数;反之亦然。
所以,可以用一句话概括extern“C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。
我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
实现C++与C及其它语言的混合编程。
明白了C++中extern"C"的设立动机,我们下面来具体分析extern"C"通常的使用技巧:
extern"C"的惯用法
(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern"C"
{
#include"cExample.h"
}
而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern"C"声明,在.c文件中包含了extern"C"时会出现编译语法错误。
C++引用C函数例子工程中包含的三个文件的源代码如下:
/*c语言头文件:
cExample.h*/
#ifndefC_EXAMPLE_H
#defineC_EXAMPLE_H
externintadd(intx,inty);
#endif
/*c语言实现文件:
cExample.c*/
#include"cExample.h"
intadd(intx,inty)
{
returnx+y;
}
//c++实现文件,调用add:
cppFile.cpp
extern"C"
{
#include"cExample.h"
}
intmain(intargc,char*argv[])
{
add(2,3);
return0;
}
如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern"C"{ }。
(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern"C",但是在C语言中不能直接引用声明了extern"C"的该头文件,应该仅将C文件中将C++中定义的extern"C"函数声明为extern类型。
C引用C++函数例子工程中包含的三个文件的源代码如下:
//C++头文件cppExample.h
#ifndefCPP_EXAMPLE_H
#defineCPP_EXAMPLE_H
extern"C"intadd(intx,inty);
#endif
//C++实现文件cppExample.cpp
#include"cppExample.h"
intadd(intx,inty)
{
returnx+y;
}
/*C实现文件cFile.c
/*这样会编译出错:
#include"cExample.h"*/
externintadd(intx,inty);
intmain(intargc,char*argv[])
{
add(2,3);
return0;
}
16.请简述以下两个for循环的优缺点
for(i=0;i { if(condition) DoSomething(); else DoOtherthing(); } if(condition) { for(i=0;i DoSome
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 经典 CC 笔试