最常见的50个C、C面试题.pdf

上传人:大米 文档编号:571552720 上传时间:2024-08-11 格式:PDF 页数:26 大小:1.06MB
返回 下载 相关 举报
最常见的50个C、C面试题.pdf_第1页
第1页 / 共26页
最常见的50个C、C面试题.pdf_第2页
第2页 / 共26页
最常见的50个C、C面试题.pdf_第3页
第3页 / 共26页
最常见的50个C、C面试题.pdf_第4页
第4页 / 共26页
最常见的50个C、C面试题.pdf_第5页
第5页 / 共26页
点击查看更多>>
资源描述

《最常见的50个C、C面试题.pdf》由会员分享,可在线阅读,更多相关《最常见的50个C、C面试题.pdf(26页珍藏版)》请在金锄头文库上搜索。

1、 C/C+经典面试题 面试题 1:变量的声明和定义有什么区别 为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。 说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。 面试题 2:写出 bool 、int、 float、指针变量与“零值”比较的 if 语句 bool 型数据: if( flag ) A; else B; int 型数据: if( 0 != flag ) A; else B; 指针型数: if(

2、 NULL = flag ) A; else B; float 型数据: if ( ( flag = NORM ) & ( flag = NORM ) ) A; NULL与0写在前面 2 注意:应特别注意在 int、指针型变量和“零值”比较的时候,把“零值”放在左边,这样当把“=”误写成“=”时,编译器可以报错,否则这种逻辑错误不容易发现,并且可能导致很严重的后果。 面试题 3:sizeof 和 strlen 的区别 sizeof 和 strlen 有以下区别: sizeof 是一个操作符,strlen 是库函数。 sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结

3、尾为0的字符串作参数。 编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。并且 sizeof计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。 数组做 sizeof 的参数不退化,传递给 strlen 就退化为指针了。 注意:有些是操作符看起来像是函数,而有些函数名看起来又像操作符,这类容易混淆的名称一定要加以区分,否则遇到数组名这类特殊数据类型作参数时就很容易出错。最容易混淆为函数的操作符就是 sizeof。 面试题 4:C 语言的关键字 static 和 C+ 的关键字 static 有什么区别 在 C 中 static

4、 用来修饰局部静态变量和外部静态变量、函数。而 C+中除了上述功能外,还用来定义类的成员变量和函数。即静态成员和静态成员函数。 注意:编程时 static 的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而 C+的静态成员则可以在多个对象实例间进行通信,传递信息。 面试题 5:中的 malloc 和中的 new 有什么区别 malloc 和 new 有以下不同: (1)new、delete 是操作符,可以重载,只能在 C+中使用。 (2)malloc、free 是函数,可以覆盖,C、C+中都可以使用。 (3)new 可以调用对象的构造函数,对应的 delete 调用相应的

5、析构函数。 (4)malloc 仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数 (5)new、delete 返回的是某种数据类型指针,malloc、free 返回的是 void 指针。 注意:malloc 申请的内存空间要用 free 释放,而 new 申请的内存空间要用 delete 释放,不要混用。因为两者实现的机理不同。 面试题 6:写一个“标准”宏 MIN #define min(a,b)(a)=(b)?(a):(b) 注意:在调用时一定要注意这个宏定义的副作用,如下调用: (+*p)=(x)?(+*p):(x)。 p 指针就自加了两次,违背了 MIN 的本意。 3 面试

6、题 7:一个指针可以是 volatile 吗 可以,因为指针和普通变量一样,有时也有变化程序的不可控性。常见例:子中断服务子程序修改一个指向一个 buffer 的指针时,必须用 volatile 来修饰这个指针。 说明:指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整型数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。 面试题 8:a 和&a 有什么区别 请写出以下代码的打印结果,主要目的是考察 a 和&a 的区别。 #include void main( void ) int a5=1,2,3,4,5; int *ptr=(int *)(&a+1);

7、printf(%d,%d,*(a+1),*(ptr-1); return; 输出结果:2,5。 注意:数组名 a 可以作数组的首地址,而&a 是数组的指针。思考,将原式的 int *ptr=(int *)(&a+1);改为 int *ptr=(int *)(a+1);时输出结果将是什么呢? 面试题 9:简述 C、C+程序编译的内存分配情况 C、C+中内存分配方式可以分为三种: (1)从静态存储区域分配: 内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static 变量等。 (2)在栈上分配: 在执行函数时,函数内局部变量的

8、存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3)从堆上分配: 即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定, 使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。 一个 C、C+程序编译时内存分为 5 大存储区:堆区、栈区、全局区、文字常量区、程序代码区。 volatile 告诉编译器i是随时可

9、能发生变化的,每次使用它的时候必须从i的地址中读取对数组名取地址为二级指针,即数组的指针 4 面试题 10:简述 strcpy、sprintf 与 memcpy 的区别 三者主要有以下不同之处: (1) 操作对象不同, strcpy 的两个操作对象均为字符串,sprintf 的操作源对象可以是多种数据类型,目的操作对象是字符串, memcpy 的两个对象就是两个任意可操作的内存地址, 并不限于何种数据类型。 (2)执行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。 (3)实现功能不同,strcpy 主要实现字符串变量间的拷贝,sprintf 主要实现其他数据类型

10、格式到字符串的转化,memcpy 主要是内存块间的拷贝。 说明:strcpy、sprintf 与 memcpy都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来选择合适的函数实现拷贝功能。 面试题 11:设置地址为 0x67a9 的整型变量的值为 0xaa66 int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa66; 说明: 这道题就是强制类型转换的典型例子, 无论在什么平台地址长度和整型数据的长度是一样的,即一个整型数据可以强制转换成地址指针类型,只要有意义即可。 面试题 12:面向对象的三大特征 面向对象的三大特征是封装性、继承性和多态性: 封装性

11、:将客观事物抽象成类,每个类对自身的数据和方法实行 protection(private, protected,public)。 继承性:广义的继承有三种实现形式:实现继承(使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。 多态性:是将父类对象设置成为和一个或更多它的子对象相等的技术。用子类对象给父类对象赋值之后,父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。 说明:面向对象的三个特征是实现面向对象技术的关键,每一个特征的相关技术都非常的复杂,程序员应该多看、多练。 面试题 13:C+的

12、空类有哪些成员函数 缺省构造函数。 缺省拷贝构造函数。 缺省析构函数。 缺省赋值运算符。 缺省取址运算符。 缺省取址运算符 const。 注意:有些书上只是简单的介绍了前四个函数。没有提及后面这两个函数。但后面这两个函数也是空类的默认函数。另外需要注意的是,只有当实际使用这些函数的时候,编译器才会去定义它们。 一个整形数据可以强制转换成地址指针类型 缺省的取地址运算符 5 面试题 14:谈谈你对拷贝构造函数和赋值运算符的认识 拷贝构造函数和赋值运算符重载有以下两个不同之处: (1)拷贝构造函数生成新的类对象,而赋值运算符不能。 (2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对

13、象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉 注意:当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。 面试题 15:用 C+设计一个不能被继承的类 template class A friend T; private: A() A() ; class B : virtual public A public: B() B() ; class C : virtual public B public: C() C() ; void main( void ) B b; /C c; ret

14、urn; 注意:构造函数是继承实现的关键,每次子类对象构造时,首先调用的是父类的构造函数,然后才是自己的。 面试题 16:访问基类的私有虚函数 写出以下程序的输出结果: #include class A 6 virtual void g() cout A:g endl; private: virtual void f() cout A:f endl; ; class B : public A void g() cout B:g endl; virtual void h() cout B:h endl; ; typedef void( *Fun )( void ); void main() B

15、b; Fun pFun; for(int i = 0 ; i next; /记录上次翻转后的链表 oldList- next = newHead; /将当前结点插入到翻转后链表的开头 newHead = oldList; /递归处理剩余的链表 return ( next=NULL )? newHead: reverse( t, newHead ); 说明:循环算法就是图 10.2图 10.5 的移动过程,比较好理解和想到。递归算法的设计虽有一点难度,但是理解了循环算法,再设计递归算法就简单多了。 面试题 21:简述队列和栈的异同 队列和栈都是线性存储结构,但是两者的插入和删除数据的操作不同,队

16、列是“先进先出”,栈是“后进先出”。 注意:区别栈区和堆区。堆区的存取是“顺序随意”,而栈区是“后进先出”。栈由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收。分配方式类似于链表。 它与本题中的堆和栈是两回事。 堆栈只是一种数据结构, 而堆区和栈区是程序的不同内存存储区域。 面试题 22:能否用两个栈实现一个队列的功能 结点结构体: typedef struct node int data; node *next; node,*LinkStack; 创建空栈: LinkStack C

17、reateNULLStack( LinkStack &S) S = (LinkStack)malloc( sizeof( node ) ); /申请新结点 if( NULL = S) printf(Fail to malloc a new node.n); 9 return NULL; S-data = 0; /初始化新结点 S-next = NULL; return S; 栈的插入函数: LinkStack Push( LinkStack &S, int data) if( NULL = S) /检验栈 printf(There no node in stack!); return NULL

18、; LinkStack p = NULL; p = (LinkStack)malloc( sizeof( node ) ); /申请新结点 if( NULL = p) printf(Fail to malloc a new node.n); return S; if( NULL = S-next) p-next = NULL; else p-next = S-next; p-data = data; /初始化新结点 S-next = p; /插入新结点 return S; 出栈函数: node Pop( LinkStack &S) node temp; temp.data = 0; temp.

19、next = NULL; if( NULL = S) /检验栈 printf(There no node in stack!); return temp; temp = *S; 10 if( S-next = NULL ) printf(The stack is NULL,cant pop!n); return temp; LinkStack p = S -next; /节点出栈 S-next = S-next-next; temp = *p; free( p ); p = NULL; return temp; 双栈实现队列的入队函数: LinkStack StackToQueuPush( L

20、inkStack &S, int data) node n; LinkStack S1 = NULL; CreateNULLStack( S1 ); /创建空栈 while( NULL != S-next ) /S 出栈入 S1 n = Pop( S ); Push( S1, n.data ); Push( S1, data ); /新结点入栈 while( NULL != S1-next ) /S1 出栈入 S n = Pop( S1 ); Push( S, n.data ); return S; 说明:用两个栈能够实现一个队列的功能,那用两个队列能否实现一个队列的功能呢?结果是否定的,因为

21、栈是先进后出,将两个栈连在一起,就是先进先出。而队列是现先进先出,无论多少个连在一起都是先进先出,而无法实现先进后出。 面试题 23:计算一颗二叉树的深度 深度的计算函数: int depth(BiTree T) if(!T) return 0; /判断当前结点是否为叶子结点 11 int d1= depth(T-lchild); /求当前结点的左孩子树的深度 int d2= depth(T-rchild); /求当前结点的右孩子树的深度 return (d1d2?d1:d2)+1; 注意:根据二叉树的结构特点,很多算法都可以用递归算法来实现。 面试题 24:编码实现直接插入排序 直接插入排序

22、编程实现如下: #include void main( void ) int ARRAY10 = 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 ; int i,j; for( i = 0; i 10; i+) coutARRAYi ; coutendl; for( i = 2; i = 10; i+ ) /将 ARRAY2,ARRAYn依次按序插入 if(ARRAYi ARRAYi-1) /如果 ARRAYi大于一切有序的数值, /ARRAYi将保持原位不动 ARRAY0 = ARRAYi; /将 ARRAY0看做是哨兵,是 ARRAYi的副本 j = i - 1; do /从右向

23、左在有序区 ARRAY1i-1中 /查找 ARRAYi的插入位置 ARRAYj+1 = ARRAYj; /将数值大于 ARRAYi记录后移 j- ; while( ARRAY0 ARRAYj ); ARRAYj+1=ARRAY0; /ARRAYi插入到正确的位置上 for( i = 0; i 10; i+) coutARRAYi ; coutendl; 12 注意:所有为简化边界条件而引入的附加结点(元素)均可称为哨兵。引入哨兵后使得查找循环条件的时间大约减少了一半,对于记录数较大的文件节约的时间就相当可观。类似于排序这样使用频率非常高的算法,要尽可能地减少其运行时间。所以不能把上述算法中的哨

24、兵视为雕虫小技。 面试题 25:编码实现冒泡排序 冒泡排序编程实现如下: #include #define LEN 10 /数组长度 void main( void ) int ARRAY10 = 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 ; /待排序数组 printf( n ); for( int a = 0; a LEN; a+ ) /打印数组内容 printf( %d , ARRAYa ); int i = 0; int j = 0; bool isChange; /设定交换标志 for( i = 1; i = i; j- ) /对当前无序区 ARRAYi.LEN自下向上

25、扫描 if( ARRAYj+1 ARRAYj ) /交换记录 ARRAY0 = ARRAYj+1; /ARRAY0不是哨兵,仅做暂存单元 ARRAYj+1 = ARRAYj; ARRAYj = ARRAY0; isChange = 1; /发生了交换,故将交换标志置为真 printf( n ); for( a = 0; a LEN; a+) /打印本次排序后数组内容 printf( %d , ARRAYa ); if( !isChange ) /本趟排序未发生交换,提前终止算法 break; printf( n ); return; 13 面试题 26:编码实现直接选择排序 #includes

26、tdio.h #define LEN 9 void main( void ) int ARRAYLEN= 5, 6, 8, 2, 4, 1, 9, 3, 7 ; /待序数组 printf(Before sorted:n); for( int m = 0; m LEN; m+ ) /打印排序前数组 printf( %d , ARRAYm ); for (int i = 1; i = LEN - 1; i+) /选择排序 int t = i - 1; int temp = 0; for (int j = i; j LEN; j+) if (ARRAYj ARRAYt) t = j; if (t !

27、= (i - 1) temp = ARRAYi - 1; ARRAYi - 1 = ARRAYt; ARRAYt = temp; printf( n ); printf(After sorted:n); for( i = 0; i LEN; i+ ) /打印排序后数组 printf( %d , ARRAYi ); printf( n ); 注意:在直接选择排序中,具有相同关键码的对象可能会颠倒次序,因而直接选择排序算法是一种不稳定的排序方法。在本例中只是例举了简单的整形数组排序,肯定不会有什么问题。但是在复杂的数据元素序列组合中,只是根据单一的某一个关键值排序,直接选择排序则不保证其稳定性,这

28、是直接选择排序的一个弱点。 面试题 27:编程实现堆排序 堆排序编程实现: #include 14 void createHeep(int ARRAY,int sPoint, int Len) /生成大根堆 while( ( 2 * sPoint + 1 ) Len ) int mPoint = 2 * sPoint + 1 ; if( ( 2 * sPoint + 2 ) Len ) if(ARRAY 2 * sPoint + 1 ARRAY 2 * sPoint + 2 ) mPoint = 2*sPoint+2; if(ARRAY sPoint = 0; i- ) /将 Hr0,Leng

29、ht-1建成大根堆 createHeep(ARRAY, i, Len); for ( i = Len - 1; i 0; i- ) int tmpData = ARRAY0; /与最后一个记录交换 ARRAY0 = ARRAYi; ARRAYi = tmpData; createHeep( ARRAY, 0, i ); /将 H.r0.i重新调整为大根堆 return; int main( void ) 15 int ARRAY = 5, 4, 7, 3, 9, 1, 6, 8, 2; printf(Before sorted:n); /打印排序前数组内容 for ( int i = 0; i

30、 9; i+ ) printf(%d , ARRAYi); printf(n); heepSort( ARRAY, 9 ); /堆排序 printf(After sorted:n); /打印排序后数组内容 for( i = 0; i 9; i+ ) printf( %d , ARRAYi ); printf( n ); return 0; 说明:堆排序,虽然实现复杂,但是非常的实用。另外读者可是自己设计实现小堆排序的算法。虽然和大堆排序的实现过程相似,但是却可以加深对堆排序的记忆和理解。 面试题 28:编程实现基数排序 #include #include #define LEN 8 typed

31、ef struct node /队列结点 int data; struct node * next; node,*QueueNode; typedef struct Queue /队列 QueueNode front; QueueNode rear; Queue,*QueueLink; QueueLink CreateNullQueue( QueueLink &Q) /创建空队列 Q = NULL; Q = ( QueueLink )malloc( sizeof( Queue ) ); if( NULL = Q ) printf(Fail to malloc null queue!n); re

32、turn NULL; 16 Q-front = ( QueueNode )malloc( sizeof( node ) ); Q-rear = ( QueueNode )malloc( sizeof( node ) ); if( NULL = Q-front | NULL = Q-rear ) printf(Fail to malloc a new queues fornt or rear!n); return NULL; Q-rear = NULL; Q-front-next= Q-rear; return Q; int lenData( node data, int len) /计算队列中

33、各结点的数据的最大位数 int m = 0; int temp = 0; int d; for( int i = 0; i 0) d /= 10; temp +; if( temp m ) m = temp; temp = 0; return m; QueueLink Push( QueueLink &Q , node node ) /将数据压入队列 QueueNode p1,p; p =( QueueNode )malloc( sizeof( node ) ); if( NULL = p ) printf(Fail to malloc a new node!n); return NULL;

34、p1 = Q-front; while(p1-next != NULL) p1 = p1-next; p-data = node.data; p1-next = p; p-next = Q-rear; 17 return NULL; node Pop( QueueLink &Q) /数据出队列 node temp; temp.data = 0; temp.next = NULL; QueueNode p; p = Q-front-next; if( p != Q-rear ) temp = *p; Q-front-next = p-next; free( p ); p = NULL; retu

35、rn temp; int IsEmpty( QueueLink Q) if( Q-front-next = Q-rear ) return 0; return 1; int main( void ) int i = 0; int Max = 0; /记录结点中数据的最大位数 int d = 10; int power = 1; int k = 0; node ArrayLEN =450, NULL, 32,NULL, 781,NULL, 57 ,NULL,组 145,NULL, 613,NULL, 401,NULL, 594,NULL; /队列结点数 QueueLink Queue10; fo

36、r( i = 0; i 10; i+) CreateNullQueue( Queuei); /初始化队列数组 for( i = 0; i LEN; i+) printf(%d ,Arrayi.data); printf(n); Max = lenData( Array, LEN ); /计算数组中关键字的最大位数 printf(%dn,Max); 18 for(int j = 0; j Max; j+) /按位排序 if(j = 0) power = 1; else power = power *d; for(i = 0; i LEN; i+) k = Arrayi.data /power -

37、 (Arrayi.data/(power * d) * d; Push( Queuek, Arrayi ); for(int l = 0, k = 0; l d; l+) /排序后出队列重入数组 while( IsEmpty( Queuel ) ) Arrayk+ = Pop( Queuel ); for( int t = 0; t next = NULL; 树结点入栈函数: void push_path(pPath H, pBTree T) pPath p = H-next; pPath q = H; while( NULL != p ) 20 q = p; p = p-next; p =

38、( pPath )malloc( sizeof( PATH ) ); /申请新结点 p-next = NULL; /初始化新结点 p-tree = T; q-next = p; /新结点入栈 树结点打印函数: void print_path( pPath L ) pPath p = L-next; while( NULL != p ) /打印当前栈中所有数据 printf(%d, , p-tree-data); p = p-next; 树结点出栈函数: void pop_path( pPath H ) pPath p = H-next; pPath q = H; if( NULL = p )

39、/检验当前栈是否为空 printf(Stack is null!n); return; p = p-next; while( NULL != p ) /出栈 q = q-next; p = p-next; free( q-next ); /释放出栈结点空间 q-next = NULL; 判断结点是否为叶子结点: int IsLeaf(pBTree T) return ( T-lchild = NULL )&( T-rchild=NULL ); 查找符合条件的路径: int find_path(pBTree T, int sum, pPath L) 21 push_path( L, T); re

40、cord += T-data; if( ( record = sum ) & ( IsLeaf( T ) ) ) /打印符合条件的当前路径 print_path( L ); printf( n ); if( T-lchild != NULL ) /递归查找当前节点的左孩子 find_path( T-lchild, sum, L); if( T-rchild != NULL ) /递归查找当前节点的右孩子 find_path( T-rchild, sum, L); record -= T-data; pop_path(L); return 0; 注意:数据结构一定要活学活用,例如本题,把所有的结

41、点都压入栈,而不符合条件的结点弹出栈,很容易实现了有效路径的查找。虽然用链表也可以实现,但是用栈更利于理解这个问题,即适当的数据结构为更好的算法设计提供了有利的条件。 面试题 34:写一个“标准”宏 MIN 写一个“标准”宏 MIN,这个宏输入两个参数并且返回较小的一个。 【答案】 #define min(a,b)(a)=(b)?(a):(b) 注意:在调用时一定要注意这个宏定义的副作用,如下调用: (+*p)和经常连续使用。 因此这两个操作符的返回值应该是一个仍旧支持这两个操作符的流引用。其他的数据类型都无法做到这一点。 注意:除了在赋值操作符和流操作符之外的其他的一些操作符中,如+、-、*

42、、/等却千万不能返回引用。因为这四个操作符的对象都是右值,因此,它们必须构造一个对象作为返回值。 面试题 40:简述指针常量与常量指针区别 指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。 指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。 注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。 面试题 41:数组名和指针的区别 请写出以下代码的打印结果: #include #includ

43、e void main(void) char str13=Hello world!; 23 char *pStr=Hello world!; coutsizeof(str)endl; coutsizeof(pStr)endl; coutstrlen(str)endl; coutstrlen(pStr)endl; return; 【答案】 打印结果: 13 4 12 12 注意:一定要记得数组名并不是真正意义上的指针,它的内涵要比指针丰富的多。但是当数组名当做参数传递给函数后,其失去原来的含义,变作普通的指针。另外要注意 sizeof 不是函数,只是操作符。 面试题 42:如何避免“野指针” “

44、野指针”产生原因及解决办法如下: (1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向 NULL。 (2)指针 p 被 free 或者 delete 之后,没有置为 NULL。解决办法:指针指向的内存空间被释放后指针应该指向 NULL。 (3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向 NULL。 注意: “野指针” 的解决方法也是编程规范的基本原则, 平时使用指针时一定要避免产生 “野指针” ,在使用指针前一定要检验指针的合法性。 面试题 43:常引用有什么作用 常引用的引入主要是为了避免使用变量的

45、引用时,在不知情的情况下改变变量的值。常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。 说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象,不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。 面试题 44:编码实现字符串转化为数字 编码实现函数atoi(),设计一个程序,把一个字符串转化为一个整型数值。例如数字:“5486321”,转化成字符:5486321。 【答案】 int myAtoi(const char * str) 24 int num = 0; /保存转换后的数值 int isNegative =

46、0; /记录字符串中是否有负号 int n =0; char *p = str; if(p = NULL) /判断指针的合法性 return -1; while(*p+ != 0) /计算数字符串度 n+; p = str; if(p0 = -) /判断数组是否有负号 isNegative = 1; char temp = 0; for(int i = 0 ; i 9 |temp 0) /滤除非数字字符 continue; if(num !=0 | temp != 0) /滤除字符串开始的 0 字符 temp -= 0x30; /将数字字符转换为数值 num += temp *int( pow

47、(10 , n - 1 -i) ); if(isNegative) /如果字符串中有负号,将数值取反 return (0 - num); else return num; /返回转换后的数值 注意:此段代码只是实现了十进制字符串到数字的转化,读者可以自己去实现 2 进制,8 进制,10进制,16 进制的转化。 面试题 45:简述 strcpy、sprintf 与 memcpy 的区别 三者主要有以下不同之处: (1) 操作对象不同, strcpy 的两个操作对象均为字符串,sprintf 的操作源对象可以是多种数据类型, 25 目的操作对象是字符串, memcpy 的两个对象就是两个任意可操作

48、的内存地址, 并不限于何种数据类型。 (2)执行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。 (3)实现功能不同,strcpy 主要实现字符串变量间的拷贝,sprintf 主要实现其他数据类型格式到字符串的转化,memcpy 主要是内存块间的拷贝。 说明:strcpy、sprintf 与 memcpy都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来选择合适的函数实现拷贝功能。 面试题 46:用 C 编写一个死循环程序 while(1) 说明:很多种途径都可实现同一种功能,但是不同的方法时间和空间占用度不同,特别是对于嵌入式软件,处理器速度比较慢,存储

49、空间较小,所以时间和空间优势是选择各种方法的首要考虑条件。 面试题 47:编码实现某一变量某位清 0 或置 1 给定一个整型变量 a,写两段代码,第一个设置 a 的 bit 3,第二个清 a 的 bit 3,在以上两个操作中,要保持其他位不变。 【答案】 #define BIT3 (0x1 3 ) Satic int a; 设置 a 的 bit 3: void set_bit3( void ) a |= BIT3; /将 a 第 3 位置 1 清 a 的 bit 3 void set_bit3( void ) a &= BIT3; /将 a 第 3 位清零 说明:在置或清变量或寄存器的某一位时

50、,一定要注意不要影响其他位。所以用加减法是很难实现的。 面试题 48:评论下面这个中断函数 中断是嵌入式系统中重要的组成部分, 这导致了很多编译开发商提供一种扩展让标准 C 支持中断。具体代表事实是,产生了一个新的关键字_interrupt。下面的代码就使用了_interrupt 关键字去定义一个中断服务子程序(ISR),请评论以下这段代码。 _interrupt double compute_area (double radius) double area = PI * radius * radius; printf( Area = %f, area); return area; 26 【答

51、案】 这段中断服务程序主要有以下四个问题: (1)ISR 不能返回一个值。 (2)ISR 不能传递参数。 (3)在 ISR 中做浮点运算是不明智的。 (4)printf()经常有重入和性能上的问题。 注意:本题的第三个和第四个问题虽不是考察的重点,但是如果能提到这两点可给面试官留下一个好印象。 面试题 49:构造函数能否为虚函数 构造函数不能是虚函数。而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好。析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。析构函数也可以是纯虚函数,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。

52、说明:虚函数的动态绑定特性是实现重载的关键技术,动态绑定根据实际的调用情况查询相应类的虚函数表,调用相应的虚函数。 面试题 50:谈谈你对面向对象的认识 面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。 说明:编程中接触最多的“面向对象编程技术”仅仅是面向对象技术中的一个组成部分。发挥面向对象技术的优势是一个综合的技术问题,不仅需要面向对象的分析,设计和编程技术,而

53、且需要借助必要的建模和开发工具。 1,从存储空间角度 虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用 ,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。 2,从使用 角度 虚函数主要用 于在信息不全的情况下,能使重载的函数得到对应的调用 。构造函数本身就是要初始化实例,那使用 虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。 虚函数的作用 在于通过父 类的指针或者引用 来调用 它的时候能够变成调用 子类的那个成员函数。而构造函数是在创建对象时自动调用 的,不可能通过父 类的指针或者引用 去调用 ,因此也就规定构造函数不能是虚函数。 3、构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用 去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用 析构函数。 4、从实现 上看,vbtl在构造函数调用 后才建立,因而构造函数不可能成为虚函数

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 习题/试题

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号