C/C++笔试忍法帖04——C/C++语法特性篇

1.Heap与stack的差别

Heap是堆,stack是栈。

Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。

Stack空间有限,Heap是很大的自由存储区

C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。

程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。

2.In C++, what does "explicit" mean?
what does "protected" mean?

c++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,在某些情况下,我们要求类的使用者必须显示调用类的构造函数时就需要使用explicit,反之默认类型转换可能会造成无法预期的问题。

protected控制的是一个函数对一个类的成员(包括成员变量及成员方法)的访问权限。protected成员只有该类的成员函数及其派生类的成员函数可以访问

3.在C++中有没有纯虚构造函数?

构造函数不能是虚的。只能有虚的析构函数

4.在c++的一个类中声明一个static成员变量有没有用?

在C++类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,也就是说不管创建多少对象,static修饰的变量只占有一块内存。其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。

static是加了访问控制的全局变量,不被继承。

5.C/C++编译器中虚表是如何完成的?

6.在c++中纯虚析构函数的作用是什么?请举例说明。

7.代码

void func() {

static int val;

… } 中,变量val的内存地址位于: 

A. 已初始化数据段    B.未初始化数据段      C.堆              D.栈

8.In C++, what does "explicit" mean? what does "protected" mean?

c++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,在某些情况下,我们要求类的使用者必须显示调用类的构造函数时就需要使用explicit,反之默认类型转换可能会造成无法预期的问题。

protected控制的是一个函数对一个类的成员(包括成员变量及成员方法)的访问权限。protected成员只有该类的成员函数及其派生类的成员函数可以访问

9.在C++中有没有纯虚构造函数?

构造函数不能是虚的。只能有虚的析构函数

10.在c++的一个类中声明一个static成员变量有没有用?

在C++类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,也就是说不管创建多少对象,static修饰的变量只占有一块内存。其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。

static是加了访问控制的全局变量,不被继承。

11.MFC中CString是类型安全类么?

答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换

12.C++中为什么用模板类?

答:(1)可用来创建动态增长和减小的数据结构

(2)它是类型无关的,因此具有很高的可复用性。

(3)它在编译时而不是运行时检查数据类型,保证了类型安全

(4)它是平台无关的,可移植性

13.函数模板与类模板有什么区别?

答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

14.关于内存对齐的问题以及sizeof()的输出

答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。

15.动态连接库的两种方式?

答:调用一个DLL中的函数有两种方法:

1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。

2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或LoadLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了

16.写出对应要求的指针

1) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)

2) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

3) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

1) int (*a)[10]; // A pointer to an array of 10 integers

2) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

3) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

17.关键字static的作用是什么?

这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中保持变量内容的持久

2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问,即隐藏的作用。它是一个本地的全局变量。

3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用,即隐藏的作用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

4).static的第三个作用是默认初始化为0

其实全局变量也具备这一属性,

因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下

18.关键字const是什么含意? 

我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?

const int a;   //a是一个常整型数

int const a;   //a是一个常整型数

const int *a; //一个指向常整型数的指针,整型数是不可修改的,但指针可以

int * const a; //一个指向整型数的常指针,指针指向的整型数是可以修改的,指针不可修改

int const * a const;// 一个指向常整型数的常指针,整型数不可修改,指针也是不可修改的

(主要看const靠近*还是int,不可修改的就是它)

前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:

1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

19.关键字volatile有什么含意 并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic
variables)

3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

1). 一个参数既可以是const还可以是volatile吗?解释为什么。

2). 一个指针可以是volatile 吗?解释为什么。

3). 下面的函数有什么错误:

int square(volatile int *ptr)

{

return *ptr * *ptr;

}

下面是答案:

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器类似下面的代码:

int square(volatile int *ptr)

{

int a,b;

a = *ptr;

b = *ptr;

return a * b;

}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)

{

int a;

a = *ptr;

return a * a;

}

20.What is virtual function ?what is vtable used for?

虚函数主要用于实现多态用,基类的某个函数前加个Virtual 用来告诉编译系统,遇到这个处理过程时,要等到执行时再确定到底调用哪个类的处理过程;

每一个虚函数都会有一个入口地址,虚函数表保存所有虚函数的入口地址

21.What‘s the difference between "struct" and "class" in c++?

struct成员默认类型为public,class成员默认类型为private。即为数据的封装。

如果没有多态和虚拟继承,在C++中,struct和class的存取效率完全相同!简单的说就是,存取class的data
member和非virtual function效率和struct完全相同!不管该data member是定义在基类还是派生类的。如果不是为了和C兼容,C++中就不会有struct关键字。

22.What do we need to make destructor vitual?why?

CObject的析构函数设为virtual型,则所有CObject类的派生类的析构函数都将自动变为virtual型,这保证了在任何情况下,不会出现由于析构函数未被调用而导致的内存泄露

23.What
to declare member function as const?

void fun1(int a) const;const的作用是指在该函数内部不会改变此类的成员变量(除非该成员变量定义时加上violate关键字),否则修改了该成员变量就会报错.

24.列出两个情况是必须使用成员初始化列表,而不在构造函数里面赋值

25.static_cast和dynamic_cast有什么区别?

26.namespace解决了什么问题?

27.auto_ptr是什么东西,有什么用?

28.New delete 与malloc free 的区别 (
Autodesk)

答案:用malloc 函数不能初始化对象,new 会调用对象的构造函数。Delete 会调用对象的destructor,而free 不会调用对象的destructor.

29. 有哪几种情况只能用intialization list 而不能用assignment?
(Autodesk)

答案:当类中含有const、reference 成员变量;基类的构造函数都需要参数;类中含有其他类的成员对象,而该类的构造函数都需要参数。

30. C++是不是类型安全的? (Autodesk)

答案:不是。两个不同类型的指针之间可以强制转换。C#是类型安全的。

31. main 函数执行以前,还会执行什么代码? (Autodesk)

答案:

main函数执行之前,主要就是初始化系统相关资源:

1.设置栈指针

2.初始化static静态和global全局变量,即data段的内容

3.将未初始化部分的全局变量赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容

4.运行全局构造器,估计是C++中构造函数之类的吧

5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数

(1)全局对象的析构函数会在main函数之后执行;

(2)可以用_onexit 注册一个函数,它会在main 之后执行;

32. 描述一下C++的多态 (microsoft)

答案:C++的多态表现在两个部分,一个是静态连编下的函数重载,运算符重载;动态连编下的虚函数、纯虚函数(抽象类)

33.请说出const 与#define 相比优点

答案:

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

(2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

34.指针和数组的区别

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(1)修改内容上的差别

char a[] = “hello”;

a[0] = ‘X’;

char *p = “world”; // 注意p 指向常量字符串

p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

char a[] = "hello world";

char *p = a;

cout<< sizeof(a) << endl; // 12 字节

cout<< sizeof(p) << endl; // 4 字节

计算数组和指针的内存容量

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4 字节而不是100 字节

}

35.类成员函数的重载、覆盖和隐藏区别

答案:

成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

36.如何打印出当前源文件的文件名以及源文件的当前行号?

答案:

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

37.main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

答案:可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void);

void main( void )

{

String str("zhanglin");

_onexit( fn1 );

_onexit( fn2 );

_onexit( fn3 );

_onexit( fn4 );

printf( "This is executed first./n" );

}

int fn1()

{

printf( "next./n" );

return 0;

}

int fn2()

{

printf( "executed " );

return 0;

}

int fn3()

{

printf( "is " );

return 0;

}

int fn4()

{

printf( "This " );

return 0;

}

The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit
cannot take parameters.

38.如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

答案:

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif

39.堆栈溢出一般是由什么原因导致的?

动态申请的内存忘记释放,即new和delete没有成对出现。

层次太深的递归调用

40. 写出float
x 与“零值”比较的if语句。

if(x>-0.000001 && x<0.000001)

41. 一语句实现x是否为2的若干次幂的判断

位运算法:

int i = 512;  cout << boolalpha
<< ((i & (i - 1)) ? false : true) << endl;

42.交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;

有两种解法, 一种用算术算法, 一种用^(异或)

a = a + b;

b = a - b;

a = a - b;

or

a = a^b;// 只能对int,char..

b = a^b;

a = a^b;

or

a ^= b ^= a;

43.c和c++中的struct有什么不同?

c和c++中struct的主要区别是c中的struct不可以含有成员函数,而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private

44.纯虚函数如何定义?使用时应注意什么?

virtual void f()=0;

是接口,子类必须要实现

45.要对绝对地址0x100000赋值,我们可以用

(unsigned int*)0x100000 = 1234;

那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

*((void (*)( ))0x100000 ) ( );

首先要将0x100000强制转换成函数指针,即:

(void (*)())0x100000

然后再调用它:

*((void (*)())0x100000)();

用typedef可以看得更直观些:

typedef void(*)() voidFuncPtr;

*((voidFuncPtr)0x100000)();

46.已知一个数组table,用一个宏定义,求出数据的元素个数

#define NTBL

#define NTBL (sizeof(table)/sizeof(table[0]))

47.请写一个C函数,若处理器是Big_endian的则返回0;若是Little_endian的则返回1

int checkCPU()

{

    union w

   {

int a;

     
char b;

}c;

   c.a = 1;

   return (c.b == 1);

 }

}

48.C++中引用与指针的区别;

答:1 引用实际上是所引用的对象或变量的别名,而指针是包含所指向对象或变量的地址的变量。

2 引用在定义时必须初始化,而指针在定义时不初始化。

3 不可以有努NULL的引用,而可以有指向NULL的指针。

4 引用在初始化后不可以改变引用关系,而指针可以随时指向其他对象(非const指针)。

49.以下关键字的含义与用法:

extern,extern “C”,static,explicit,register,#undef,#ifndef

在C++中引用C语言中的函数变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"

{

#i nclude "cExample.h"

}

#undef 是在后面取消以前定义的宏定义

50.从语法上,在C++中(只讨论C++中)。class和struct做类型定义时只有两点区别:

(一)默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;

(二)成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。

51.下面函数的返回值(微软)

int func(x)

{

int countx = 0;

while(x)

{

countx ++;

x = x&(x-1);

}

return countx;

}

假定x = 9999。 答案:8

思路:将x转化为2进制,看含有的1的个数。

52.交换两个数的宏定义

交换两个参数值的宏定义为:. #define SWAP(a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);

53.下面的代码输出是什么,为什么?

void foo(void)

{

unsigned int a = 6;

int b = -20;

(a+b > 6) puts("> 6") : puts("<= 6");

}

这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6

54.结构与联合有和区别?

(1).结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。

(2).对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

55.宏与内联函数的区别

1.内联函数在运行时可调试,而宏定义不可以;

2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;

3.内联函数可以访问类的成员变量,宏定义则不能;

4.在类中声明同时定义的成员函数,自动转化为内联函数。

56.头文件中的 ifndef/define/endif 干什么用?

答:防止该头文件被重复引用。

57.#include   <filename.h>    和   #include   “filename.h” 有什么区别?(5分)

答:对于#include   <filename.h> ,编译器从标准库路径开始搜索 filename.h

对于#include  “filename.h” ,编译器从用户的工作路径开始搜索 filename.h

58.const 有什么用途?(请至少说明两种)(5分)

答:(1)可以定义 const 常量

(2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

59.在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”? (5分)

答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。

C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。

60.关于C语言的内存的思考


void GetMemory(char *p)

{

p = (char *)malloc(100);

}

void Test(void)

{

char *str = NULL;

GetMemory(str);

strcpy(str, "hello world");

printf(str);

}

请问运行Test函数会有什么样的结果?

答:程序崩溃。

因为GetMemory并不能传递动态内存,

Test函数中的 str一直都是 NULL。

strcpy(str, "hello world");将使程序崩溃。


char *GetMemory(void)

{

char p[] = "hello world";

return p;

}

void Test(void)

{

char *str = NULL;

str = GetMemory();

printf(str);

}

请问运行Test函数会有什么样的结果?

答:可能是乱码。

因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。


void GetMemory2(char **p, int num)

{

*p = (char *)malloc(num);

}

void Test(void)

{

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, "hello");

printf(str);

}

请问运行Test函数会有什么样的结果?

答:

(1)能够输出hello

(2)内存泄漏


void Test(void)

{

char *str = (char *) malloc(100);

strcpy(str, “hello”);

free(str);

if(str != NULL)

{

strcpy(str, “world”);

printf(str);

}

}

请问运行Test函数会有什么样的结果?

答:篡改动态内存区的内容,后果难以预料,非常危险。

因为free(str);之后,str成为野指针,

if(str != NULL)语句不起作用。

61.编写类String的构造函数、析构函数和赋值函数(25分)

已知类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的上述4个函数。

标准答案:

// String的析构函数

String::~String(void)                // 3分

{

delete [] m_data;

// 由于m_data是内部数据类型,也可以写成 delete m_data;

}

// String的普通构造函数

String::String(const char *str)       // 6分

{

if(str==NULL)

{

m_data = new char[1];     // 若能加 NULL 判断则更好

*m_data = ‘ 0’;

}

else

{

int length = strlen(str);

m_data = new char[length+1];   // 若能加 NULL 判断则更好

strcpy(m_data, str);

}

}

// 拷贝构造函数

String::String(const String &other)    // 3分

{

int length = strlen(other.m_data);

m_data = new char[length+1];       // 若能加 NULL 判断则更好

strcpy(m_data, other.m_data);

}

// 赋值函数

String & String::operate =(const String &other)     // 13分

{

// (1) 检查自赋值                      // 4分

if(this == &other)

return *this;

// (2) 释放原有的内存资源             // 3分

delete [] m_data;

// (3)分配新的内存资源,并复制内容 // 3分

int length = strlen(other.m_data);

m_data = new char[length+1];          // 若能加 NULL 判断则更好

strcpy(m_data, other.m_data);

// (4)返回本对象的引用             // 3分

return *this;

}

62.编写strcpy函数(10分)

已知strcpy函数的原型是

        char *strcpy(char *strDest, const char *strSrc);

        其中strDest是目的字符串,strSrc是源字符串。

(1)不调用C++/C的字符串库函数,请编写函数 strcpy

char *strcpy(char *strDest, const char *strSrc);

{

assert((strDest!=NULL) && (strSrc !=NULL)); // 2分

char *address = strDest;                    // 2分

while( (*strDest++ = * strSrc++) != ‘\ 0’ )     // 2分

;

return address ;                           // 2分

}

(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?

答:为了实现链式表达式。                                               // 2分

例如        int length = strlen( strcpy( strDest, “hello world”) );

63.测试方法

人工测试:个人复查、抽查和会审

机器测试:黑盒测试和白盒测试

64.以下三条输出语句分别输出什么?

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”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。

65. 非C++内建类型A和B,在哪几种情况下B能隐式转化为A?

a.
class B :
public A { ……}
// B公有继承自A,可以是间接继承的

b.
class B {
operator A( ); }
// B实现了隐式转化为A的转化

c.
class A { A(
const B& ); }
// A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数

d. A&
operator= (
const B& ); //
赋值操作,虽不是正宗的隐式类型转换,但也勉强算一个

66. 以下代码有什么问题?

cout << (true?1:"0")
<< endl;

答:三元表达式“?:”问号后面的两个操作数必须为同一类型。

67. 以下反向遍历array数组的方法有什么错误?

vector
array;

array.push_back( 1 );

array.push_back( 2 );

array.push_back( 3 );

for( vector::size_type i=array.size()-1; i>=0; --i )
// 反向遍历array数组

{

cout <<
array[i] << endl;

}

答:首先数组定义有误,应加上类型参数:vector<int> array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i为0时再减就会变成最大的整数,导致循环失去控制。

68. C++中的空类,默认产生哪些类成员函数?

class Empty

{

public:

Empty();                //
缺省构造函数

Empty(
const Empty& ); 
// 拷贝构造函数

~Empty();               //
析构函数

Empty&
operator=(
const Empty& ); 
// 赋值运算符

Empty*
operator&();                //
取址运算符

const Empty*
operator&() const;   
// 取址运算符const

};

69. 以下两条输出语句分别输出什么?

float a = 1.0f;

cout << (int)a << endl;

cout << (int&)a << endl;

cout << boolalpha << ( (int)a == (int&)a
) << endl; // 输出什么?

float b = 0.0f;

cout << (int)b << endl;

cout << (int&)b << endl;

cout << boolalpha << ( (int)b == (int&)b
) << endl; // 输出什么?

答:分别输出false和true。注意转换的应用。(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是,(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。因为以整数形式存放和以浮点形式存放其内存数据是不一样的,因此两者不等。对b的两种转换意义同上,但是的整数形式和浮点形式其内存数据是一样的,因此在这种特殊情形下,两者相等(仅仅在数值意义上)。

注意,程序的输出会显示(int&)a=1065353216,这个值是怎么来的呢?前面已经说了,以浮点数形式存放在内存中,按ieee754规定,其内容为x0000803F(已考虑字节反序)。这也就是a这个变量所占据的内存单元的值。当(int&)a出现时,它相当于告诉它的上下文:“把这块地址当做整数看待!不要管它原来是什么。”这样,内容x0000803F按整数解释,其值正好就是(十进制数)。

通过查看汇编代码可以证实“(int)a相当于重新构造了一个值等于a的整型数”之说,而(int&)的作用则仅仅是表达了一个类型信息,意义在于为cout<<及==选择正确的重载版本。

70. 以下代码有什么问题?

typedef vector IntArray;

IntArray
array;

array.push_back( 1 );

array.push_back( 2 );

array.push_back( 2 );

array.push_back( 3 );

//
删除array数组中所有的2

for( IntArray::iterator itor=array.begin(); itor!=array.end();
++itor )

{

if( 2 == *itor )
array.erase( itor );

}

答:同样有缺少类型参数的问题。另外,每次调用“array.erase(itor);”,被删除元素之后的内容会自动往前移,导致迭代漏项,应在删除一项后使itor--,使之从已经前移的下一个元素起继续遍历。

71. 写一个函数,完成内存之间的拷贝。[考虑问题是否全面]

void* mymemcpy(
void *dest,
const void *src, size_t count )

{

char* pdest =
static_cast<char*>( dest );

const char* psrc =
static_cast<const
char*>( src );

if( pdest>psrc && pdest<psrc+count )
// 能考虑到这种情况就行了,即pdest和psrc之间内存距离的不够宽,则从psrc的最后一个开始拷贝到pdest的最后一个内存位置里去。

{

for( size_t i=count-1; i!=-1; --i )

pdest[i] = psrc[i];

}

else

{

for( size_t i=0; i<count; ++i )

pdest[i] = psrc[i];

}

return dest;

}

C/C++笔试忍法帖04——C/C++语法特性篇,布布扣,bubuko.com

时间: 2024-10-29 12:03:51

C/C++笔试忍法帖04——C/C++语法特性篇的相关文章

C/C++笔试忍法帖00——开始索引篇

即将出去找个C++的实习,网上随便一搜C++笔试题,网友们整理的一套套的笔试题目,看了大有收获,原来自己还差的远呢,即便是学习过的,也不一定能回答的出来.所以看这些题目,不仅可以学到新的东西,可以起到复习基础的作用. 为此,绿整理并分类了自己已经看过一遍的C++相关的笔试题,分类成如下: 1.系统篇(涉及进程,线程等问题等) 2.网络篇(涉及各种网络协议,基础概念等) 3.数据库篇(设计数据库的概念的解释等) 4.C/C++语法特性篇(涉及堆栈,多态,内存分配,编译器特性,注意事项,技巧编程,容

C/C++笔试忍法帖01——系统篇

1.进程和线程的差别. 线程是指进程内的一个执行单元,也是进程内的可调度实体. 与进程的区别: (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位 (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行 (3)拥有资源:进程是拥有资源的独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源. (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销. 2.如果只想让程序有一个实例运行,不能运行两个.

C/C++笔试忍法帖03——数据库篇

1.存储过程是什么?有什么用?有什么优点? 存储过程(Stored Procedure)是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它. 存储过程用于实现频繁使用的查询.业务规则.被其他过程使用的公共例行程序. 存储过程在创建时即在服务器上进行编译,所以执行起来比单个 SQL 语句快. 2.一般数据库若出现日志满了,会出现什么情况,是否还能使用? 答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任

C/C++笔试忍法帖02——网络篇

1.网络编程中设计并发服务器,使用多进程与多线程 ,请问有什么区别 1,进程:子进程是父进程的复制品.子进程获得父进程数据空间.堆和栈的复制品. 2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列. 区别:两者都可以提高程序的并发度,提高程序运行效率和响应时间.线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护:而进程正相反.同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移. 2.描述三

C/C++笔试忍法帖05——数据结构篇

1.写出下列算法的时间复杂度. (1)冒泡排序: O(n^2) (2)选择排序: 直接选择排序是O(n^2) (3)插入排序:直接插入排序是 O(n^2) (4)快速排序: O(nlog2 n) (5)堆排序:   O(nlog2 n) (6)归并排序: O(nlog2 n) 2.编程,请实现一个c语言中类似atoi的函数功能(输入可能包含非数字和空格) #include <stdio.h> int isspace(int x) { if ((x == 0)||(x == '\t') || (

ASP.NET Web API实践系列04,通过Route等特性设置路由

ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程.在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一文中,体验了通过模版.惯例.HTTP方法来设置路由,这种做法的好处是把路由模版统一放在了App_Start文件夹下的WebApiConfig类中,方便管理,但缺点是不够灵活. REST把一切都看成资源,有时候,一个资源连带子资源,比如Customer和Orders密切关联,我们可能希望输入这样的请求:cust

【程序员笔试面试复习】之一 网络与通信篇(一) 几大网络模型:OSI、TCP/IP、B/S与C/S、MVC结构

9.1网络模型 9.1.1. OSI七层模型 OSI(Open System Interconnection,开放系统互联)七层网络模型称为开放式网络互联参考模型.其为国际标准组织指定的一个指导信息互联.互通和协作的网络规范. 开放是指只要遵循OSI标准,位于世界上任何地方的任何系统之间都可以进行通信,开放系统是指遵循互联协议的实际系统,如电话系统. 从逻辑上可以将OSI开放系统互联分为七层模型,由下至上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 其中,上三层称为高层,用

[Android 编译(一)] Ubuntu 16.04 LTS 成功编译 Android 6.0 源码教程

本文转载自:[Android 编译(一)] Ubuntu 16.04 LTS 成功编译 Android 6.0 源码教程 1 前言 经过3天奋战,终于在Ubuntu 16.04上把Android 6.0的源码编译出来了,各种配置,各种error,各种爬坑,特写此博客记录爬坑经历.先上图,Ubuntu上编译完后成功运行模拟器,如图: 2 编译环境 UbuntuKylin 16.04 LTS Android 6.0_r1 Open JDK 7 3 准备工作 (1) 下载android 6.0源码.

【转】C/C++ 笔试、面试题目大汇总

1.求下面函数的返回值( 微软) int func(x) { int countx =0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定x = 9999. 答案:8 思路:将x转化为2进制,看含有的1的个数. 2. 什么是“引用”?申明和使用“引用”要注意哪些问题? 答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同.申明一个引用的时候,切记要对其进行初始化.引用声明完毕后,相当于目标变量