C/C++ 经典面试题汇总

面试题1:变量的声明和定义有什么区别 ?

为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。  

说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。

面试题2:写出bool 、int、 float、指针变量与“零值”比较的if语句

bool型数据:  if(flag) { A; } else { B;}
int型数据:   if(0==flag) { A; } else { B; }
指针变量:     if(NULL==flag) { A; } else { B; }
float型数据: #define NORM (0.000001) 
             if((flag>=-NORM) && (flag<=NORM)) { A; } else { B; }

面试题3:sizeof和strlen的区别?

sizeof和strlen有以下区别:  ? 

1sizeof:一个运算符
   strlen:一个库函数

2sizeof:参数可以是类型、变量
   strlen:只能以字符串作参数
   
3sizeof:类型做参数时,可以省略括号
   strlen:参数必须加括号
   
4sizeof:计算的是数据类型占内存的大小
   strlen:计算的是字符串实际的长度
   
5sizeof:编译阶段计算出结果
   strlen:运行时阶段才计算出结果
   
6sizeof:数组做参数时不退化
   strlen:数组做参数时退化为指针
   
7、其他
 
注意:有些是操作符看起来像是函数,而有些函数名看起来又像操作符,这类容易混淆的名称一定要加以区分,否则遇到数组名这类特殊数据类型作参数时就很容易出错。最容易混淆为函数的操作符就是sizeof。

面试题4:C语言的关键字 static 和 C++ 的关键字 static 有什么区别?

在C中static用来修饰局部静态变量和外部静态变量、函数。而C++中除了上述功能外,还用来定义类的成员变量和函数。即静态成员和静态成员函数。  
 
注意:编程时static的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而C++的静态成员则可以在多个对象实例间进行通信,传递信息。

面试题5:C语言中的malloc和C++中的new有什么区别?

malloc和new有以下不同:  
1new、delete 是操作符,可以重载,只能在C++中使用。 
2、malloc、free是函数,可以覆盖,C、C++中都可以使用。  
3new 可以调用对象的构造函数,对应的delete调用相应的析构函数。 
4、malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数  
5new、delete返回的是某种数据类型指针,malloc、free返回的是void指针。  

注意:malloc申请的内存空间要用free释放,而new申请的内存空间要用delete释放,不要混用。因为两者实现的机理不同。

面试题6:写一个“标准”宏MIN

#define MIN(a,b) ((a)<=(b)?(a):(b))

面试题7:一个指针可以是volatile吗?

可以,因为指针和普通变量一样,有时也有变化程序的不可控性。常见例:子中断服务子程序修改一个指向一个buffer的指针时,必须用volatile来修饰这个指针。  

说明:指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整型数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。

面试题8:a和&a有什么区别,请写出以下代码的打印结果

#include<stdio.h>

void main( void )
{
    int a[5]= {1,2,3,4,5};
    int *ptr=(int *)(&a+1);
    printf("%d,%d",*(a+1),*(ptr-1));
    return;
}
输出结果:2,5  

注意:数组名a可以作数组的首地址,而&a是数组的指针。

思考,将原式的int *ptr=(int *)(&a+1);改为int *ptr=(int *)(a+1);时
输出结果将是: 2,1

面试题9:简述C、C++程序编译的内存分配情况

一个C、C++程序编译时内存分为5大存储区:堆区、栈区、全局区、文字常量区、程序代码区。

C、C++中内存分配方式可以分为三种:  
1、从静态存储区域分配:  
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static变量等。  
2、在栈上分配: 
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
3、从堆上分配:  
即动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

面试题10:简述strcpy、sprintf与memcpy的区别

三者主要有以下不同之处:  

1、操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。 
2、执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。 
3、实现功能不同,strcpy主要实现字符串变量间的拷贝,sprintf主要实现其他数据类型格式到字符串的转化,memcpy主要是内存块间的拷贝。  

说明:strcpy、sprintf与memcpy都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来选择合适的函数实现拷贝功能。

面试题11:设置地址为0x67a9的整型变量的值为0xaa66

int *ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;

面试题12:面向对象的三大特征

面向对象的三大特征是封装性、继承性和多态性: 
 ? 
封装性:将客观事物抽象成类,每个类对自身的数据和方法实行protection(privateprotectedpublic)。 ? 
继承性:广义的继承有三种实现形式:实现继承(使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。  ? 
多态性:是将父类对象设置成为和一个或更多它的子对象相等的技术。用子类对象给父类对象赋值之后,父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。

面试题13:C++的空类有哪些成员函数

1、缺省构造函数 ? 
2、缺省拷贝构造函数? 
3、缺省析构函数 
4、缺省赋值运算符? 
5、缺省取址运算符  ? 
6const

面试题14:谈谈你对拷贝构造函数和赋值运算符的认识

拷贝构造函数和赋值运算符重载有以下两个不同之处:
 
1、拷贝构造函数生成新的类对象,而赋值运算符不能。 
2、由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉

面试题15:简述类成员函数的重写、重载和隐藏的区别

1、重写和重载主要有以下几点不同:
  ? 
范围的区别:被重写的和重写的函数在两个类中,而重载和被重载的函数在同一个类中。  ? 
参数的区别:被重写函数和重写函数的参数列表一定相同,而被重载函数和重载函数的参数列表一定不同。  ? 
virtual的区别:重写的基类中被重写的函数必须要有virtual修饰,而重载函数和被重载函数可以被virtual修饰,也可以没有。  

2、隐藏和重写、重载有以下几点不同:  ? 

范围的不同:和重写一样,隐藏函数和被隐藏函数不在同一个类中。  ? 
参数的区别:隐藏函数和被隐藏的函数的参数列表可以相同,也可不同,但是函数名肯定要相同。 当参数不相同时,无论基类中的参数是否被virtual修饰,基类的函数都是被隐藏,而不是被重写。 


说明:虽然重载和覆盖都是实现多态的基础,但是两者实现的技术完全不相同,达到的目的也是完全不同的,覆盖是动态态绑定的多态,而重载是静态绑定的多态。

面试题16:简述多态实现的原理

编译器发现一个类中有虚函数,便会立即为此类生成虚函数表 vtable。虚函数表的各表项为指向对应虚函数的指针。编译器还会在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)指向虚函数表。调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable,将类与此类的vtable联系了起来。另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable。如此才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。  

注意:一定要区分虚函数,纯虚函数、虚拟继承的关系和区别。牢记虚函数实现原理,因为多态C++面试的重要考点之一,而虚函数是实现多态的基础。

面试题17:链表和数组有什么区别?

数组和链表有以下几点不同: 
 
1、存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。  
2、数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低。  
3、数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动。  
4、越界问题:链表不存在越界问题,数组有越界问题。  

说明:在选择数组或链表数据结构时,一定要根据实际需要进行选择。数组便于查询,链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。

面试题 18:简述队列和栈的异同

相同:队列和栈都是线性存储结构
不同:插入和删除数据的操作不同,队列是“先进先出”,栈是“后进先出”

面试题19:编码实现冒泡排序

void Bubble(int array[], int size)
{
    int i,j;

    for(i=0; i<size; i++)
    {
        for(j=i+1; j<size; j++)
        {
            if(array[i]>array[j])
            {
                array[i]=array[i]^array[j];
                array[j]=array[i]^array[j];
                array[i]=array[j]^array[i];
            }
        }
    }
}

面试题20:编码实现直接选择排序

void Sort(int array[], int size)
{
    int i,j,k;

    for(i=0; i<size; i++)
    {
        k=i;
        for(j=i+1; j<size; j++)
        {
            if(array[j]<array[k])
            {
                k=j;
            }
        }

        if(i!=k)
        {
            array[i]=array[i]^array[k];
            array[k]=array[k]^array[i];
            array[i]=array[i]^array[k];
        }
    }
}

面试题21:谈谈你对编程规范的理解或认识

编程规范可总结为:程序的可行性,可读性、可移植性以及可测试性。  

说明:这是编程规范的总纲目,面试者不一定要去背诵上面给出的那几个例子,应该去理解这几个例子说明的问题,想一想,自己如何解决可行性、可读性、可移植性以及可测试性这几个问题,结合以上几个例子和自己平时的编程习惯来回答这个问题。

面试题22:short i = 0; i = i + 1L;这两句有错吗?

代码一是错的,代码二是正确的。  

说明:在数据安全的情况下大类型的数据向小类型的数据转换一定要显示的强制类型转换。
 

自动转换遵循以下规则:

1) 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。

2) 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。

     a.若两种类型的字节数不同,转换成字节数高的类型

     b.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

3) 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。

4) char型和short型参与运算时,必须先转换成int型。

5) 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。

面试题23:&&和&、||和|有什么区别?

1、&和|对操作数进行求值运算,&&和||只是判断逻辑关系
2、&&和||在在判断左侧操作数就能确定结果的情况下就不再对右侧操作数求值

面试题24:C++的引用和C语言的指针有什么区别?

1、引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。  
2、修改区别:引用初始化以后不能被改变,指针可以改变所指的对象。  
3、非空区别:不存在指向空值的引用,但是存在指向空值的指针。  
4、合法性区别:使用引用前不需检测合法性,指针应该被检测,防止为空
5、应用区别:当可能指向空对象或根据需要指向不同对象时,用指针,否则应该用引用

注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能会引发错误。所以使用时一定要小心谨慎。

面试题25:typedef和define有什么区别?

1、用法不同:typedef用来定义一种数据类型的别名,增强程序的可读性。define主要用来定义常量,以及书写复杂使用频繁的宏。  
2、执行时间不同:typedef是编译过程的一部分,有类型检查的功能。define是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
3、作用域不同:typedef有作用域限定。define不受作用域约束,只要是在define声明后的引用都是正确的。  
4、对指针的操作不同:typedef和define定义的指针时有很大的区别。  

注意:typedef定义是语句,因为句尾要加上分号。而define不是语句,千万不能在句尾加分号。

面试题26:关键字const是什么?

const用来定义一个只读的变量或对象。
主要优点:便于类型检查、同宏定义一样可以方便地进行参数的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。  

说明:const修饰函数参数,是一种编程规范的要求,便于阅读,一看即知这个参数不能被改变,实现时不易出错。

面试题27:static有什么作用?

static在C中主要用于定义全局静态变量、定义局部静态变量、定义静态函数。在C++中新增了两种作用:定义静态数据成员、静态函数成员。

面试题28:extern有什么作用?

extern标识的变量或者函数声明其定义在别的文件中,提示编译器遇到此变量和函数时在其它模块中寻找其定义。

面试题29:流操作符重载为什么返回引用?

在程序中,流操作符>>和<<经常连续使用。因此这两个操作符的返回值应该是一个仍旧支持这两个操作符的流引用。其他的数据类型都无法做到这一点。

面试题30:简述指针常量与常量指针区别

指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。
常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。  

指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。 

注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。

面试题31:数组名和指针的区别

请写出以下代码的打印结果:

#include <iostream.h>
#include <string.h>
void main(void)
{
    char str[13]="Hello world!";
    char *pStr="Hello world!";
    cout<<sizeof(str)<<endl;
    cout<<sizeof(pStr)<<endl;
    cout<<strlen(str)<<endl;
    cout<<strlen(pStr)<<endl;
    return;
}
答案:
13
4
12
12

面试题32:如何避免“野指针”

“野指针”产生原因及解决办法如下:  

1、指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向NULL。  
2、指针被 free 或者 delete 之后,没有置NULL。解决办法:指针指向的内存空间被释放后指针应该指向NULL。  
3、指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。

面试题33:常引用有什么作用?

常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。 

说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象,不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。

面试题34:用C编写一个死循环程序

while(1) { }
 
说明:很多种途径都可实现同一种功能,但是不同的方法时间和空间占用度不同,特别是对于嵌入式软件,处理器速度比较慢,存储空间较小,所以时间和空间优势是选择各种方法的首要考虑条件。

面试题35:编码实现某一变量某位清0或置1

#define BIT3 (0x1 << 3 )
      Satic int a;

/*将a第3位置1*/
void set_bit3( void )
{
    a |= BIT3;
}
/*将a第3位清零*/
void set_bit3( void )
{
    a &= ~BIT3;
}

面试题36:评论下面这个中断函数

中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展——让标准
C支持中断。具体代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义一个中断服务子程序(ISR),请评论以下这段代码。
__interrupt double compute_area (double radius)
{
    double area = PI * radius * radius;
    printf(" Area = %f", area);
    return area;
}
主要有以下四个问题: 

1、ISR 不能返回一个值。  
2、ISR 不能传递参数。  
3、在ISR 中做浮点运算是不明智的。 
4、printf()经常有重入和性能上的问题。

面试题37:构造函数能否为虚函数?

构造函数不能是虚函数。而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好。析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。析构函数也可以是纯虚函数,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
面试题38:谈谈你对面向对象的认识
面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。

面试题39:What will be the output of the following C code?[中国台湾某著名CPU生产公司2005年面试题]

#include<stdio.h>
#define product(x) (x*x)
int main()
{
    int i=3,j,k;
    j=product(i++);
    k=product(++i);
    printf("j=%d,k=%d",j,k);
    return 0;
}
答案:j=9,k=49

面试题40:What will be the output of the following C code?[中国台湾某著名CPU生产公司2005年面试题]

int i=1,j=2;
int k=i+++j;
cout<<k<<endl;
答案:3

面试题41:What will be the output of the following C code?[中国台湾某著名CPU生产公司2005年面试题]

char foo(void)
{
    unsigned int a=6;
    int b=-20;
    char c;
    
    (a+b>6)?(c=1):(c=0);
    return c;
}
答案:1

面试题42:编码实现字符串转化为数字

/*扫描字符串,跳过空格,遇上数字或正负符号开始做转换,遇到非数字或字符串结束时(‘/0‘)才结束转换并返回结果*/

int myAtoi(const char * str)
{

    #define FALSE (0)
    #define TRUE (1)
    int sum=0,begin=0,i;
    int minus=FALSE;
    while(*str!=\0)
    {
        if(begin==0 && ((isdigit(*str)) || *str==-) )
        {
            begin=1;
            if(*str == -)
            {
                minus=TRUE;
                str++;
            }
        }
        else if(begin==1 && !isdigit(*str))
        {
            break;
        }

        if(begin==1)
        {
            sum=sum*10+(*str-0);
        }

        str++;
    }

    return minus? -sum : sum;
}

面试题43:Which of the following statements describe the results of executing the code snippet below in C++? (下列C++代码的输出结果是什么?)[台湾某著名杀毒软件公司2010年7月笔试题]

int i = 1;

void main()

{

    int i = i;

}

A. The i within main will have an undefined value. (main()里的i是一个未定义值)

B. The i within main will have a value of 1.(main()里的i值为1)

C. The compiler will not allow this statement. (编译器不允许这种写法)

D. The i within main will have a value of 0. (main()里的i值为0)
答案:A

面试题44:What does the following program print?(下面程序的结果是多少?)[中国台湾某著名计算机硬件公司2005年12月面试题]

#include <iostream>

using namespace std;

int main()

{

    int x=2,y,z;

    x *=(y=z=5);
    cout << x << endl;

    z=3;

    x ==(y=z);
    cout << x << endl;

    x =(y==z);
    cout << x << endl;

    x =(y&z);
    cout << x << endl;

    x =(y&&z);
    cout << x << endl;

    y=4;

    x=(y|z);
    cout << x << endl;

    x=(y||z);
    cout << x << endl;

    return 0;

}
答案:
10
10
1
3
1
7
1

面试题45:以下代码结果是多少?[中国某杀毒软件公司2010年3月笔试题]

#include   <iostream>

using namespace std;

int func(int x)
{
    int count = 0;

    while(x)
    {
        count ++;
        x=x&(x-1);
    }   

    return count;
}       

int main()
{
    cout << func(9999) << endl;

    return 0;
}
答案:8
 
解析:本题func函数返回值是形参x转化成二进制后包含1的数量。理解这一点就很容易答出来了。9999转化为二进制是:9999:10011100001111

面试题46:编程实现十进制转换成二进制

#include <stdio.h>
#include <stdlib.h>

void Binary(int decimal)
{
    unsigned int i=sizeof(int)*8;

    while(i>0)
    {
        printf("%d",(decimal>>(i-1)&1));
        i--;
    }
}

int main()
{
    int num;
    puts("please input a num:");
    scanf("%d", &num);

    Binary(num);

    return 0;
}

面试题47:What does the keyword "const" means in C program? Please at least make two examples about
the usages of const.[美国某著名计算机嵌入式公司2005面试题]

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

面试题48:const与#define相比有什么不同?

1、 编译器处理方式不同
  define宏是在预处理阶段展开。
  const常量是编译运行阶段使用。
2、类型和安全检查不同
  define宏没有类型,不做任何类型检查,仅仅是展开。
  const常量有具体的类型,在编译阶段会执行类型检查。
3、存储方式不同
  define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
  const常量会在内存中分配(可以是堆中也可以是栈中)。
4、const  可以节省空间,避免不必要的内存分配
5、 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
6、C++语言可以用const定义常量,也可以用#define定义常量,但是前者比后者有更多的优点:
7、有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

面试题49:What will happen after running the "Test"?

#include <iostream.h>

void GetMemory(char *p, int num)
{
    p = (char *)malloc(sizeof(char) * num);
}

int main()
{
    char *str = NULL;

    GetMemory(str, 100);
    strcpy(str, "hello");

    return 0;
}
答案:程序崩溃。因为GetMemory并不能传递动态内存,Test函数中的str一直都是NULL。

面试题50:如何在C中为一个数组分配空间?

一种是栈的形式:char array[5];
一种是堆的形式:char *array; array=(char *)malloc(sizeof(char)*5);//C++: array=new char[5];
 
提示:第一种形式空间分配的大小可能会受操作系统的限制,比如windows会限制在2M;第二种形式成空间分配很灵活,想分配多少分配多少,只要RAM够大。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。