博文
C/C++ 试题剖析(5)(2006-11-09 22:58:00)
摘要:试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的://pStr是指向以'\0'结尾的字符串的指针//steps是要求移动的nvoid 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 ); } 剖析: 这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。 最频繁被使用的库函数包括: (1) strcpy (2) memcpy (3) memset 试题6:已知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
通道数
1......
C/C++ 试题剖析(4)(2006-11-09 22:48:00)
摘要:试题1:分别给出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) 剖析: 考查对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:以下为Windows NT下的32位C++程序,请计算sizeof的值void Func ( char str[100] ){ sizeof( str ) = ?}void *p = malloc( 100 );sizeof ( p ) = ? 解答:sizeof( str ) = 4sizeof ( p ) = 4 剖析: Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。 数组名的本质如下: (1)数组名指代一种数据结构,这种数据结构就是数组; 例如:char str[10];cout << sizeof(str) << endl; 输出结果为10,str指代数据结构char[10]。 (2)数组名可以转换为指向其指代实体的指......
C/C++ 试题剖析(3)(2006-11-09 22:46:00)
摘要:试题4:void GetMemory( char *p ){ p = (char *) malloc( 100 );}void Test( void ) { char *str = NULL; GetMemory( str ); strcpy( str, "hello world" ); printf( str );} 试题5:char *GetMemory( void ){ char p[] = "hello world"; return p; }void Test( void ){ char *str = NULL; str = GetMemory(); printf( str ); } 试题6:void GetMemory( char **p, int num ){ *p = (char *) malloc( num );}void Test( void ){ char *str = NULL; GetMemory( &str, 100 ); strcpy( str, "hello" ); printf( str ); } 试题7:void Test( void ){ char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str ); ... //省略的其它语句} 解答: 试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完char *str = NULL;GetMemory( str ); 后的str仍然为NULL; 试题5中char p[] = "hello world"; return p; 的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。 试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句*p = (char *) malloc( num ); 后未判断内存是否申请成功,应加上:if ( *p == NULL ){ ...//进行申请内存失败处理} 试题7存在与试题6同样的问题,在执行c......
C/C++ 试题剖析(2)(2006-11-09 22:44:00)
摘要:试题3:void test3(char* str1){ char string[10]; if( strlen( str1 ) <= 10 ) { strcpy( string, str1 ); }}
if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。
考查对基本功的掌握: (1)字符串以’\0’结尾; (2)对数组越界把握的敏感度; (3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案: 2分void strcpy( char *strDest, char *strSrc ){ while( (*strDest++ = * strSrc++) != ‘\0’ );} 4分void strcpy( char *strDest, const char *strSrc ) //将源字符串加const,表明其为输入参数,加2分{ while( (*strDest++ = * strSrc++) != ‘\0’ );} 7分void strcpy(char *strDest, const char *strSrc) { //对源地址和目的地址加非0断言,加3分 assert( (strDest != NULL) && (strSrc != NULL) ); while( (*strDest++ = * strSrc++) != ‘\0’ );} 10分//为了实现链式操作,将目的地址返回,加3分!char * strcpy( char *strDest, const char *strSrc ) { assert( (strDest != NULL) && (strSrc != NULL) ); char *address = strDest; while( (*strDest++ = * strSrc++) != ‘\0’ ); return address;} (4)对strlen的掌握,它没有包括字符串末尾的'\0'。 一个10分的strlen函数了,完美的版本为:
int strlen( const char *str ) ......
strcpy()与strlen()、sizeof()的区别(2006-11-09 22:28:00)
摘要:char s1[6]="china" ,s2[3]="ch";strcpy(s1,s2);请问内存中ina部分的数据是否被覆盖??S1的内容是ch,还是china??S1的长度是6??
strcpy(s1,s2); strcpy函数的意思是:把字符串s2中的内容copy到s1中,连字符串结束标志也一起copy.这样s1在内存中的存放为:ch\0如果说s1的长度是6,那是错误的.你没有弄清strlen与sizeof的意思。strlen函数的意思是测试字符串的字符长度,不含字符串结束标志的。sizeof是个运算符,它的结果是字符串在内存中的所占字节大小,它要把\0算进去的。......
C/C++ 试题剖析(1)(2006-11-09 22:17:00)
摘要:试题1:void test1(){ char string[10]; char* str1 = "0123456789"; strcpy( string, str1 );}
试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
试题2:void test2(){ char string[10], str1[10]; int i; for(i=0; i<10; i++) { str1[i] = 'a'; } strcpy( string, str1 );}
指出字符数组str1不能在数组内结束可以给3分;
指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分
在此基础上指出库函数strcpy工作方式的给10分;......
《高级继承》学习笔记(2006-10-22 20:52:00)
摘要:1.一个类的成员数据有可能包含另一个类的对象。C++程序员称其为外层类包容内层类。
2.私有继承不涉及继承接口,只涉及继承实现。
3.当派生类是一种基类时必须使用公有继承。
当你想把某些功能授权给另一个类而不必访问其保护型成员时一定要使用包容。
当你需要根据一个类而实现另一个类时,并且需要访问基类的保存型成员时使用私有继承。
当需要使用多个基类时,不能使用私有继承,必须使用包容。
当基类的成员不能用于派生类时,不能使用公有继承。
4.如果要把一个类的私有成员数据暴露给另一个类,那么就应把这个类声明为一个友元。
友元关系是不可以传递的,也不可以继承,友元关系也不是互通的。
友元类的声明:
friend class newclass;
友元声明可以放在类声明的任何地方。声明之后,本类的任何成员数据就完全暴露给另一个类。
也可以把另一个类的成员函数声明为该类的友元函数。......
《静态成员变量及函数》学习笔记(2006-10-21 20:38:00)
摘要:1.类的静态成员变量在所有类的实例中是共享的。它是全局数据与成员数据的折中。可以把静态成员变量看成是属于类而不是属于对象的。因此,对类的实例对象中并不会为静态成员变量留出存储空间。
一个常见的错误是忘记定义类的静态成员变量。若HowmanyCats是类Cat的静态成员变量,则其定义句为:
int Cat::HowmanyCats=0;
此外,若该静态成员变量是私有的,那么类必须为该静态成员变量提供一个公有的访问函数。对于没有对象的静态成员,则可以定义一个不属于类的全局函数来访问该成员变量。因此可以在没有对象的情况下访问类的静态成员变量。
2.静态成员函数没有this指针,因此它们不能声明为const,且静态成员函数不能访问任何非静态成员变量。
3.如同数组名,函数名是指向函数的常指针。函数指针的声明如下所示:
long (*FunPtr) (int);
第一对括号必须有,否则就不是函数指针的定义,而是一个返回指针的函数。使用函数指针就如同使用函数名一样。
FunPtr(x);
和
(*FunPtr)(x);
一样,前一种只是后一种的简化形式。
也可以把函数指针作为函数变量来传递。例如:
void Func (void ( * ) (int &, int &) , int &, int &);
可以用typedef来定义结构 void ( * ) (int &,int &)
typedef (*VPF) (int &,int &)
则 VPF可以用来代替以上结构体。
4.为创建成员函数指针,可以使用与函数指针创建相同的语法,但要包含类名和作用域运算符(::)。如:
void (Shape::*pFunc)(int ,int);
使用方法一样,只是需要一个类的对象。......
《多态性》学习笔记(2006-10-19 20:56:00)
摘要:1.可以从多个基类中派生出一个新类。这就叫做多重继承。如:
class newClass:public class1,public class2
{};
2.在内存中创建newClass的对象时,两个基类形成newClass对象的组成部分。
3.多重继承对象中的构造函数。若每个基类都有带参数的构造函数,那么newClass将依次初始化这些构造函数。
4.虚继承。如:
class newClass:virtual public baseClass
通常一个类的构造函数只初始化自己的成员变量和基类。而虚继承的基类则是一个例外,它由派生性最强的类初始化。
为了确保派生类只有一个公共基类的一个实例,把中间类声明为从基类的虚基类。因此虚继承适用于新类的多个基类的基类是同一个类的情况。
5.当一个新类需要从多个基类中继承函数和特征时必须用多重继承。
当派生性最强的类必须只有共享基类的一个实例时必须用虚继承。
6.一个抽象数据类型是一个概念,而不是一个对象。在C++中一个ADT总是其他类的基类,生成ADT的实例是非法的。
C++支持用纯虚函数来创建抽象数据类型。一个虚函数通过初始化为0就变成了纯虚函数。如:
virtual void Draw()=0;
任何带有一个或多个纯虚函数的类就叫做ADT。对于带有纯虚函数的类,其派生类必须覆盖纯虚函数。否则,它的派生类也是ADT,也不能实例化。
然而也可以给纯虚函数提供实现代码,然后这个函数可以被由ADT派生的对象所调用,这样做可能是为了给所覆盖的函数提供公共功能。......
《数组》学习笔记(2006-10-18 21:02:00)
摘要:1.数组自0开始,而不是1,这点初学者容易混淆。对于越过数组尾进行写操作的错误,称作“篱笆标志错误”。
数组声明大小必须为常量,不可以是变量。
2.初始化数组。在等号之后的大括号之内用一组用逗号隔开的数来初始化数组。如:
int IntergerArray[3]={10,20,30};
数组大小可以忽略。如:
int IntergerArray[ ] ={10,20,30};
不可以初始化比声明的数组大小更多的数组元素。
对声明 int Array[5]={10,20};这里只初始化了前两个元素。
3.对多维数组进行初始化。如:
int Array[2][3]={1,2,3,4,5,6};
其中前三个数进入Array[0],后三个数进入Array[1]。以上还可以写作
int Array[2][3]={{1,2,3},{4,5,6}};
4.在自由存储区里声明数组。如:
int *ptr=new int [arrayNum];
这里arrayNum可以是常值,也可以是变量。
删除自由存储区中的数组用
delete [] ptr;
如果不加方括号,那么删掉的只是数组的第一个元素。
5.一个数组名是指向数组第一个元素的常量指针。
6.字符数组。初始化可以用一个一个字符来进行,也可以用如下方法:
char array[ ]="Hello World!";
应该注意两点:
1)应该用带有双引号的字符串,没有逗号和大括号。
2)不用添加空字符,因为编译器会自动添加。
7.函数strcpy()、strncpy()是将一个字符串拷贝到另一个字符串中。其中strcpy()是把字符串的全部内容复制到一个指定的缓冲区中。要使用这些函数,必须包括头文件(string.h)。
strcpy(string2,string1);
将字符串1复制到2中,注意缓冲区2的大小到足够。......
