经典基础C++笔试题(附答案)


一、选择题 1. 若用数组名作为函数调用时的实参,则实际上传递给形参的是( A )
A. 数组首地址
B. 数组的第一个元素值
C. 数组中全部元素的值
D. 数组元素的个数
2. 有关函数重载的正确说法是( B )
A. 函数名不同,但参数的个数和类型相同
B. 函数名相同,但参数的个数不同或参数的类型不同
C. 函数名相同,参数的个数和类型相同
D. 函数名相同,函数的返回值不同,而与函数的参数和类型无关
3. 已知int a[3][2]={3,2,1}; 则表达式“a[0][0]/a[0][1]/a[0][2]”的值是(B)
A. 0.166667
B. 1
C. 0
D.错误的表达式
4. 要禁止修改指针p本身,又要禁止修改p所指向的数据,这样的指针应定义为( D )
A. const char *p="ABCD";
B. char const *p="ABCD";
C. char *const p="ABCD";
D. const char * const p="ABCD";
5. 对静态成员的不正确描述是( C)
A. 静态成员不属于对象,是类的共享成员
B. 静态数据成员要在类外定义和初始化
C. 调用静态成员函数时要通过类或对象激活,所以静态成员函数拥有this指针
D. 非静态成员函数也可以操作静态数据成员
6. 下面函数原型声明中,( B)声明了fun为纯虚函数
A. void fun()=0;
B. virtual void fun()=0;
C. virtual void fun();
D. virtual void fun(){};
7. 在排序方法中,关键码比较次数与记录地初始排列无关的是( D )
A. Shell排序
B. 归并排序
C. 直接插入排序
D. 选择排序
8. 一个栈的入栈序列是A,B,C,D,E,则栈的不可能的输出序列是( C )
A. EDCBA;
B. DECBA;
C. DCEAB;
D. ABCDE



二、填空题
1.求下面函数的返回值 _____

int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; }




假定x = 9999。 答案:8
思路:将x转化为2进制,看含有的1的个数。
2. 以下三条输出语句分别输出什么 _____
char str1[] = "abc"; char str2[] = "abc"; const char str3[] = "abc"; const char str4[] = "abc"; const char* str5 = "abc"; const char* str6 = "abc"; cout << boolalpha << ( str1==str2 ) << endl; // 输出什么? cout << boolalpha << ( str3==str4 ) << endl; // 输出什么? cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?




答:分别输出false,false,true。str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;str3和str4同上,只是按const语义,它们所指向的数据区不能修改。str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。
3.请给出如下程序的结果
int a = 3; int b = a << 3;



3*2^3 = 24
a = __3__ , b = __24__ 。
4.请给出如下程序的结果
#define MAX_NUM 100+200 int nTemp = MAX_NUM*10;




则 Temp = __2100__ 。
5. 程序的运行结果为___
void Func(char str[100]) { printf("%d\n", sizeof(str)); }



数组作为函数的形参退化为指针
答:4
分析: 指针长度
6. 程序的运行结果为____
int sum(int a) { auto int c=0; static int b=3; c+=1; b+=2; return(a+b+c); } void main() { int I; int a=2; for(I=0; I<5; I++) { printf("%d,", sum(a)); } }




// static会保存上次结果,记住这一点,剩下的自己写
输出:8,10,12,14,16,
7. 程序的运行结果为____
unsigned short array[]={1,2,3,4,5,6,7}; int i = 3; *(array + i) = ?



答: 4



8.写出以下程序的执行结果:

static char *s[]= {"black","white","yellow","violet"}; char **ptr[]={s+3,s+2,s+1,s},***p=ptr; **++p; printf("%s\n",*++*++p+3);


思路:*++(*++p); // s+1 -> s+2 即 "white" -> "yellow"

答:low
分析:注意指针++的区别,以及*p+1与*(p+1)的区别

三、问答题 1、头文件中的 ifndef/define/endif 干什么用? 答:防止该头文件被重复引用。

2. C++中为什么用模板类。答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

3. 说一说C与C++的内存分配方式? (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配(动态内存分配)程序在运行的时候用malloc或new申请任意多少的内存,程序员负责在何时用free或delete释放内存。动态内存的生存期自己决定,使用非常灵活。

4. 关键字static的作用是什么?这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
(1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
(2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
(3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

5. 面向对象的三个基本特征,并简单叙述之? (1)封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public)
(2) 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承 (仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
(3)多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

6. 局部变量能否和全局变量重名? 答:能,局部会屏蔽全局。要用全局变量,需要使用"::" ; 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

7. 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别? 答:从定义上来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义复类虚函数的方法。
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。
重写:当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。

8. new delete 与malloc free 的联系与区别? 答案:都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.

9. 什么是死锁?其条件是什么?怎样避免死锁?
答案:死锁的概念:在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。
死锁产生的原因主要是:? 系统资源不足;? 进程推进顺序非法。
产生死锁的必要条件:
(1)互斥(mutualexclusion),一个资源每次只能被一个进程使用;
(2)不可抢占(nopreemption),进程已获得的资源,在未使用完之前,不能强行剥夺;
(3)占有并等待(hold andwait),一个进程因请求资源而阻塞时,对已获得的资源保持不放;
(4)环形等待(circularwait),若干进程之间形成一种首尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
死锁的处理策略:鸵鸟策略、预防策略、避免策略、检测与恢复策略。

10. ARP 是地址解析协议,简单语言解释一下工作原理。 答:
( 1 )首先,每个主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址之间的对应关系。
( 2 )当源主机要发送数据时,首先检查 ARP 列表中是否有对应 IP 地址的目的主机的 MAC 地址,如果有,则直接发送数据,如果没有,就向本网段的所有主机发送 ARP 数据包,该数据包包括的内容有:源主机 IP 地址,源主机 MAC 地址,目的主机的 IP 地址。
( 3 )当本网络的所有主机收到该 ARP 数据包时,首先检查数据包中的 IP 地址是否是自己的 IP 地址,如果不是,则忽略该数据包,如果是,则首先从数据包中取出源主机的 IP 和 MAC 地址写入到 ARP 列表中,如果已经存在,则覆盖,然后将自己的 MAC 地址写入 ARP 响应包中,告诉源主机自己是它想要找的 MAC 地址。
( 4 )源主机收到 ARP 响应包后。将目的主机的 IP 和 MAC 地址写入 ARP 列表,并利用此信息发送数据。如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。
广播发送 ARP 请求,单播发送 ARP 响应。

11、请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句#define Max(a,b)( a/b)?a:b

12. 分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。 答案:

BOOL :if ( !a ) or if(a) int :if ( a == 0) float :const EXPRESSION EXP = 0.000001 if ( a < EXP && a >-EXP) pointer : if ( a != NULL) or if(a == NULL)





13. 已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy。
答案:

/* 编写strcpy函数(10分) 已知strcpy函数的原型是 char *strcpy(char *strDest, const char *strSrc); 其中strDest是目的字符串,strSrc是源字符串。 (1)不调用C++/C的字符串库函数,请编写函数 strcpy (2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值? 答:为了 实现链式表达式。 // 2分 例如 int length = strlen( strcpy( strDest, “hello world”) ); */ #include #include char*strcpy(char*strDest, constchar*strSrc) { assert((strDest!=NULL) && (strSrc !=NULL)); // 2分 char* address = strDest; // 2分 while( (*strDest++=*strSrc++) !='\0' )// 2分 NULL; return address ; // 2分 }





14. 已知链表的头结点head(有内容),写一个函数把这个链表逆序
Node * ReverseList(Node *head) //链表逆序 { if ( head == NULL || head->next == NULL ) return head; Node *p1 = head ; Node *p2 = p1->next ; Node *p3 = p2->next ; p1->next = NULL ; // 头结点的next指向NULL while ( p3 != NULL ) { p2->next = p1 ; // 逆序 p1 = p2 ; // 三个指针分别后移一位 p2 = p3 ; p3 = p3->next ; } p2->next = p1 ; head = p2 ; // 作为新的头结点 return head ; }






15. 写个函数交换两个指针。 答案 :
#include using namespace std; void ex(char **a,char **b) { char *c; c=*a; *a=*b; *b=c; } int main() { char *pt=”pt\n”; char *pt_another=”pt_another\n”; cout<




【经典基础C++笔试题(附答案)】


    推荐阅读