该试卷转载自林锐《高质量C/C++编程指南》,先贴下原作者的版权声明。
版权声明:本书的大部分内容取材于作者一年前的书籍手稿(尚未出版),现整理汇编成为上海贝尔网络应用事业部的一个规范化文件,同时作为培训教材。由于C++/C编程是众所周知的技术,没有秘密可言。编程的好经验应该大家共享,我们自己也是这么学来的。作者愿意公开本书的电子文档。版权声明如下:(1)读者可以任意拷贝、修改本书的内容,但不可以篡改作者及所属单位。(2)未经作者许可,不得出版或大量印发本书。(3)如果竞争对手公司的员工得到本书,请勿公开使用,以免发生纠纷。预计到2002年7月,我们将建立切合中国国情的CMMI3级解决方案。届时,包括本书在内的约1000页规范将严格受控。欢迎读者对本书提出批评建议。林锐,2001年7月
附录B:C++/C试题
本试题仅用于考查C++/C程序员的基本编程技能。内容限于C++/C常用语法,不涉及数据结构、算法以及深奥的语法。考试成绩能反映出考生的编程质量以及对C++/C的理解程度,但不能反映考生的智力和软件开发能力。笔试时间90分钟。请考生认真答题,切勿轻视。
一、请填写BOOL,float,指针变量与“零值”比较的if语句。(10分)
提示:这里“零值”可以是0,0.0,FALSE或者“空指针”。例如int变量n与“零值”比较的if语句为:
if(n==0)
if(n!=0)
以此类推。
请写出BOOL flag与“零值”比较的if语句:
请写出float x与“零值”比较的if语句:
请写出char* p与“零值”比较的if语句:
二、以下为WindowsNT下的32位C++程序,请计算sizeof的值(10分)
char str[]=“Hello”;
char* p=str;
int n=10;
请计算:
sizeof(str)=
sizeof(p)=
sizeof(n)=
void Func(char str[100])
{
请计算sizeof(str)=
}
void* p=malloc(100);
请计算sizeof(p)=
三、简答题(25分)
1、头文件中的ifndef/define/endif干什么用?
2、#include<filename.h>和#include“filename.h”有什么区别?
3、const有什么用途?(请至少说明两种)
4、在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”声明?
5、请简述以下两个for循环的优缺点。
第一个:
for(i=0;i<N;i++)
{if(condition)DoSomething();elseDoOtherthing();
}
优点:
缺点:
if(condition)
{for(i=0;i<N;i++)DoSomething();
}
else
{for(i=0;i<N;i++)DoOtherthing();
}
优点:
缺点:
四、有关内存的思考题(20分)
void GetMemory(char* p)
{p=(char*)malloc(100);
}
void Test(void)
{char* str=NULL;GetMemory(str);strcpy(str,"hello world");print(str);
}
请问运行Test函数会有怎样的结果?
void* GetMemory(void)
{char p[]="hello world";return p;
}
void Test(void)
{char* str=NULL;str=GetMemory();print(str);
}
请问运行Test函数会有怎样的结果?
void GetMemory2(char **p,int num)
{*p=(char*)malloc(num);
}
void Test(void)
{char* str=NULL;GetMemory(&str,100);strcpy(str,"hello");print(str);
}
请问运行Test函数会有怎样的结果?
void Test(void)
{char* str=(char*)malloc(100);strcpy(str,"hello");free(str);if(str!=NULL){strcpy(str,"world");print(str);}
}
请问运行Test函数会有怎样的结果?
五、编写strcpy函数(10分)已知strcpy函数的原型是charstrcpy(charstrDest,const char*strSrc);其中strDest是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写函数strcpy
(2)strcpy能把strSrc的内容复制到strDest,为什么还要char*类型的返回值?
六、编写类String的构造函数、析构函数和赋值函数(25分)
已知类String的原型为:
classString
{public:String(const char* str=NULL); //普通构造函数String(const String &other);//拷贝构造函数~String(void);//析构函数String & operate = (const String &other);//赋值函数private:char* m_data;//用于保存字符串
};
请编写String的上述4个函数。
附录B:C++/C试题(答案)
本试题仅用于考查C++/C程序员的基本编程技能。内容限于C++/C常用语法,不涉及数据结构、算法以及深奥的语法。考试成绩能反映出考生的编程质量以及对C++/C的理解程度,但不能反映考生的智力和软件开发能力。笔试时间90分钟。请考生认真答题,切勿轻视。
一、请填写BOOL,float,指针变量与“零值”比较的if语句。(10分)
提示:这里“零值”可以是0,0.0,FALSE或者“空指针”。例如int变量n与“零值”比较的if语句为:
if(n==0)
if(n!=0)
以此类推。
请写出BOOL flag与“零值”比较的if语句:
标准答案:
if(flag)
if(!flag)
如下写法均属不良风格,不得分。
if(flag==TRUE)
if(flag==1)
if(flag==FALSE)
if(flag==0)
我的答案:属于不良风格……0分if(flag!=TRUE)if(flag==FALSE)
请写出float x与“零值”比较的if语句:
标准答案示例:
constfloatEPSINON=0.00001;
if((x>=-EPSINON)&&(x<=EPSINON)
不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此类形式。
我的答案:是错误的写法,0分。if(x==0.0)if(x!=0.0)
请写出char* p与“零值”比较的if语句:
标准答案:
if(p==NULL)
if(p!=NULL)
我的答案:3分if(p==NULL)if(p!=NULL)
二、以下为WindowsNT下的32位C++程序,请计算sizeof的值(10分)
char str[]=“Hello”;
char* p=str;
int n=10;
请计算:
sizeof(str)=6 我的答案:正确 2分
sizeof(p)=4 我的答案:正确 2分
sizeof(n)=4 我的答案:正确 2分
void Func(char str[100])
{
请计算sizeof(str)=4 我的答案:sizeof(str)=100 错误 0分 这题太粗心了……
}
void* p=malloc(100);
请计算sizeof(p)=4 我的答案:正确 2分
三、简答题(25分)
1、头文件中的ifndef/define/endif干什么用?
标准答案:
答:防止该头文件被重复引用。
我的答案:
答:总体来说,头文件中的ifndef/define/endif是防止头文件在被重复引用时多次进行调用而加的限制。ifndef就是指该头文件如果被定义,就define定义以下头文件的内容直到endif结束。这样当下一次重复引用该头文件的时候,ifndef到endif之间的内容就不再被执行。
2、#include<filename.h>和#include“filename.h”有什么区别?
标准答案:
答:对于#include<filename.h>,编译器从标准库路径开始搜索filename.h对于#include“filename.h”,编译器从用户的工作路径开始搜索filename.h
我的答案:
答:#include<filename.h>是直接从编译器库的安装路径查找头文件名字,若没有则报错。#include“filename.h”则是先在工程文件目录下查找头文件名字,若没有再从编译器库的安装路径查找头文件名字。
3、const有什么用途?(请至少说明两种)
标准答案:
答:(1)可以定义const常量(2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
我的答案:
答:一、定义常量,例如const int a;定义后a的值无法被变更。
二、修饰函数的形参,在执行函数时加了const修饰的形参所对应的实参值不会被变更,使得程序更加健壮。
三、修饰指针,常量指针和指针常量。
4、在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”声明?
标准答案:
答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(intx,inty);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
我的答案:
答:因为C++编译器无法直接编译.c文件,需要加extern“C”声明后编译器才会将extern“C”声明的内容通过C语言的方式进行编译。
5、请简述以下两个for循环的优缺点。
第一个:
for(i=0;i<N;i++)
{if(condition)DoSomething();elseDoOtherthing();
}
标准答案:
优点:程序简洁
缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。
我的答案:
优点:代码量较少
缺点:循环体每进行一次,if,else就会进行一次重复的条件判断,造成资源浪费。
第二个:
if(condition)
{for(i=0;i<N;i++)DoSomething();
}
else
{for(i=0;i<N;i++)DoOtherthing();
}
标准答案:
优点:循环的效率高
缺点:程序不简洁
我的答案:
优点:资源浪费相对第一种较少
缺点:代码量较大
四、有关内存的思考题(20分)
void GetMemory(char* p)
{p=(char*)malloc(100);
}
void Test(void)
{char* str=NULL;GetMemory(str);strcpy(str,"hello world");print(str);
}
请问运行Test函数会有怎样的结果?
标准答案:
答:程序崩溃。因为GetMemory并不能传递动态内存,Test函数中的str一直都是NULL。strcpy(str,“helloworld”);将使程序崩溃。
我的答案:
答:会造成程序崩溃,因为函数GetMemory并没有真正的将str指针的地址传过去,str仍然是空指针,直接对空指针进行操作会造成程序崩溃。
void* GetMemory(void)
{char p[]="hello world";return p;
}
void Test(void)
{char* str=NULL;str=GetMemory();print(str);
}
请问运行Test函数会有怎样的结果?
标准答案:
答:可能是乱码。因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原现的内容已经被清除,新内容不可知。
我的答案:
答:会造成非法访问。函数结束后,p指针所在内存已经被释放,不能再对其指针的内容进行访问。
void GetMemory2(char **p,int num)
{*p=(char*)malloc(num);
}
void Test(void)
{char* str=NULL;GetMemory(&str,100);strcpy(str,"hello");print(str);
}
请问运行Test函数会有怎样的结果?
标准答案:
答:(1)能够输出hello(2)内存泄漏
我的答案:
答:会造成内存泄漏,因为malloc之后没有进行free。
void Test(void)
{char* str=(char*)malloc(100);strcpy(str,"hello");free(str);if(str!=NULL){strcpy(str,"world");print(str);}
}
请问运行Test函数会有怎样的结果?
标准答案:
答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str!=NULL)语句不起作用。
我的答案:
答:会造成非法访问,free之后,str指针所指的空间已被释放,不能再对其进行访问。而free之后str本身不会变成NULL。
五、编写strcpy函数(10分)已知strcpy函数的原型是charstrcpy(charstrDest,const char*strSrc);其中strDest是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写函数strcpy
标准答案:
char*strcpy(char*strDest,constchar*strSrc);
{assert((strDest!=NULL)&&(strSrc!=NULL));//2分char*address=strDest;//2分while((*strDest++=*strSrc++)!=‘\0’)//2分NULL;returnaddress;//2分}
我的答案:
答:
char* my_strcpy(char*strDest,const char*strSrc)
{assert(strDest&&strSrc);char* p=strDest;while(*strDest++=*strSrc++){;}return p;
}
(2)strcpy能把strSrc的内容复制到strDest,为什么还要char*类型的返回值?
标准答案:
答:为了实现链式表达式。例如intlength=strlen(strcpy(strDest,“helloworld”));
我的答案:
答:函数返回的是源字符串的首地址。这是为了防止strSrc的首地址再后续中还会被其他函数调用。
六、编写类String的构造函数、析构函数和赋值函数(25分)
已知类String的原型为:
classString
{public:String(const char* str=NULL); //普通构造函数String(const String &other);//拷贝构造函数~String(void);//析构函数String & operate = (const String &other);//赋值函数private:char* m_data;//用于保存字符串
};
请编写String的上述4个函数。
标准答案:
//String的析构函数
String::~String(void)//3分
{delete [] m_data;//由于m_data是内部数据类型,也可以写成delete m_data;
}
我的答案:
因为还没开始对C++进行系统性学习,这里做得一塌糊涂……就不贴出来献丑了……
总结
这套题考查的是基本编程能力……但是最后得分很尴尬,如果是“有人情味”大学老师,些许能给个“60分万岁”。但如果稍微严厉一点就肯定不及格。而且其中内存那一块的题还是上C语言视频课时老师讲的原题。
看来路还很长……学无止境……
不过要是没有这2个月的学习,这套题我能拿到的分数估计只有个位数……都不知道我这4年的Qt工作是怎么做下来的……
对于考卷一些内容对ChatAI的提问:
问:const floatEPSINON=0.00001;
答:
这行代码定义了一个名为 EPSINON
的浮点型常量,并将其赋值为 0.00001
。常量使用 const
关键字来修饰,以指出它们的值不能被修改。在这个例子中,我们定义了一个用于比较两个浮点数是否相等的精度值,也可以理解为一个允许的误差范围(即在这个误差范围内,可以认为两个浮点数相等)。在很多需要进行浮点数比较的算法中,通常会定义一个这样的常量,以确保比较的正确性和精度。
问:delete [] m_data;和delete m_data;的区别
答:
在 C++ 中,当我们利用 new
操作符动态分配内存以后,就需要利用 delete
操作符来释放这些内存,以免出现内存泄漏。delete
操作符有两种形式,即:
delete
:用于释放单个对象所占用的内存。delete[]
:用于释放数组所占用的内存。
它们的区别在于,delete
释放一个单独的对象所占用的内存,而 delete[]
释放数组中的所有元素所占用的内存,包括数组对象本身。如果你使用 delete
来释放数组内存,则可能会造成内存泄漏,因为它只会释放数组对象本身的内存,而不会释放数组元素的内存,这就会导致内存泄漏。
假设你在动态分配内存时使用了 new[]
操作符,那么就应该使用 delete[]
来释放这段内存,否则会导致内存泄漏和未定义的行为。而在使用 new
来动态分配单个对象的内存时,则需要使用 delete
来释放这段内存。
在具体实现中,delete[]
操作符在释放数组时,会遍历数组的所有元素,并逐个调用元素的析构函数,然后再释放内存。而 delete
操作符只会调用单个对象的析构函数,并释放该对象的内存。因此,在释放动态分配的数组内存时,我们需要使用 delete[]
操作符,而不是 delete
操作符。
最后贴上该书作者写在导读的话:
首先请做附录B的C++/C试题(不要看答案),考查自己的编程质量究竟如何。然后参照答案严格打分。(1)如果你只得了几十分,请不要声张,也不要太难过。编程质量差往往是由于不良习惯造成的,与人的智力、能力没有多大关系,还是有药可救的。成绩越差,可以进步的空间就越大,中国不就是在落后中赶超发达资本主义国家吗?只要你能下决心改掉不良的编程习惯,第二次考试就能及格了。(2)如果你考及格了,表明你的技术基础不错,希望你能虚心学习、不断进步。如果你还没有找到合适的工作单位,不妨到上海贝尔试一试。(3)如果你考出85分以上的好成绩,你有义务和资格为你所在的团队作“C++/C编程”培训。希望你能和我们多多交流、相互促进。半年前我曾经发现一颗好苗子,就把他挖到我们小组来。(4)如果你在没有任何提示的情况下考了满分,希望你能收我做你的徒弟。