首页 > 代码库 > 笔试面试那件小事(常见的C++基础题)

笔试面试那件小事(常见的C++基础题)

1->简述变量声明和定义的区别

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

 

2->简述sizeof和strlen的区别

主要区别如下: sizeof是一个操作符,strlen是库函数;sizeof的参数可以是数据的类型,也可以是变量。而strlen的参数只能是以‘\0’结尾的字符串参数。

                              编译器在编译时就计算出了sizeof的结果,而strlen函数必须在运行时才能计算出来,并且sizeof计算出的是占用内存的大小,strlen计算的是字符串实际的长度

                             数组做sizeof的参数不会退化,传递给strlen就退化为指针。

 

3->说说C和C++中的static有什么作用。

在C中static用来修饰局部静态变量和外部静态变量、函数。而C++中除了上述功能外,还用来定义类的成员变量和函数。编程时候最常用到的是static的记忆性。

 

4->C和C++中动态内存分配的方法和区别

C里面一般使用malloc/free,C++里面可用malloc/free和new/delete.

 

5->简述C、C++程序编译的内存分配情况

主要在静态存储器,在堆上和栈上分配三种。

 

6->说说strcpy、sprintf和memcpy三个函数

函数的主要功能:

strcpy:实现字符串变量间的拷贝;sprintf:主要实现其他数据类型格式到字符串格式的转化;Memcpy:主要是内存块间的拷贝

区别:操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目标操作对象只能是字符串;memcpy:两个对象都是任意可以操作的内存地址,并不局限于某种数据类型。          执行效率不同:memcpy最高;strcpy次之;sprintf效率最低。

 

7->拷贝构造函数和赋值运算符

拷贝构造函数生成新的类对象,而赋值运算符则不能。由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不需要考虑源对象是否和新建对象相同。

 

8->用递归和非递归两种方法翻转一个链表

typedef Struct ListNode{

    Elemtype Data;

    ListNode *next;

}ListNode;

 

Void ReserveList(ListNode *&pHead){                //非递归实现

    ListNode *phead=pHead;

   ListNode  *p=pHead;

    ListNode *q=NULL;

    while(p!=NULL){

        ListNode *pNext=p->next;

        p->next=q;

        q=p;

         p=pNext;

    }

    pHead=q;

}

 

ListNode* ReserveList(ListNode *&pHead){                  //递归的方式实现

    if(!pHead  ||!pHead->next)

         return pHead;

        ListNode *p=ReserveList(pHead->next);

        pHead->next->next=pHead;

        return pHead;

}

 

9->说说引用和指针的区别

引用必须被初始化,但是不分配存储空间。指针可以先声明不初始化。在初始化时候需要分配存储空间

引用初始化后不能被改变,指针可以改变所值的对象

不存在指向空值的引用,但是存在指向空值的指针

 

10->指针常量与常量指针的区别

这是一个比较常见的问题,也就是const char *p和char *const p的差别,前者称为常量指针(指针指向的内容不变),后者是指针常量(指针本身不可再被重新赋值)

const char* p:因为const修饰在*的前面,因此const修饰的是(*p),因此修饰的是p指向的内容为常量

char const * p:这个解释同上

char* const p:const修饰的是变量p,,而变量p的类型是char*.这个意思是p的值为常量

 

11->数组名和指针的区别

指针是一个变量,有自己对应的存储空,而数组名仅仅是一个符号,不是变量,因此没有自己对应的存储空间。

1,地址相同,大小不同。采用sizeof求值,指针占的大小为4字节,sizeof(数组名)求出数组占的内存大小

2,都可以作为形参,当数组名作为形参的时候退化成指针

 

12->构造函数是否可以为虚函数,为什么?

构造函数不能是虚函数,而且不能在构造函数中调用虚函数,因为那样实际上执行的是父类对应的函数。可是自己却因此会没构造好。

析构函数可以是虚函数。

 

13->delete 与delete[]的区别?

简单的来说delete删除一个指针,而delete[]删除的是一个数组。

 

14->实现一个算法计算一个数的二进制表示方式的1的个数

int Func(x)

{

    int Ret=0;

    while(x){

        Ret++;

        x=x&(x-1);

    }

    return Ret;

}

 

15->将引用作为函数返回值类型的格式、好处和需要遵守的规则?

格式:类型标识符&   函数名(形参列表){//函数体}

好处:在内存中不产生被返回值的副本(也正是因为如此,所以返回一个局部变量的引用是不可取的,因为随着该局部变量的生存期的结束,相应的引用也会失效)

注意事项:

1:不能返回局部变量的引用。因为局部变量会在函数返回后被销毁,因此被返回的引用就成为了无所指的引用,程序会进入未知状态

2:不能返回函数内部new分配的引用。虽然吧存在局部变量的被动销毁问题,对于这种情况又会面临其他尴尬的局面。例如:被函数返回的引用只是作为一个临时变量出现,而没有被赋值给一个实际的变量,那么这个引用所指的空间就无法be释放。造成内存泄露。

3:可以返回类成员的引用,但最好用const

4:流操作符重载的返回值申明为“引用”的作用:流操作符常常被希望继续使用原来的功能,所以流操作符的返回值必须是支持原来操作的引用

5:在四则运算中不能返回引用,必须构造一个对象作为返回值,考虑可以连续赋值。

 

16->谈谈对于关联、聚合以及组合的认识

涉及UML中的一些概念:

关联是两个类的一般联系,比如“学生”和“老师”就是一种关联关系

聚合表示has-a的关系,是一种相对松散的关系,聚合不需要对被聚合类负责,用空的菱形表示聚合关系。从实现的角度来看,聚合可以表示为:Class A{,,,,} Class B{A *a,....}

组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心菱形表示组合关系。实现形式:Class A{}  Class B{A a}

 

17->当一个类C没有任何成员变量与成员函数的时候,这时sizeof(C)的值是多少,如果不是0,解释下为什么不是零

一个空类对象的大小是1Byte。这个是被编译器安插进去的一个字节,这样就使得两个空类实例在内存中得以区别。

 

18->用变量a给出下面的定义

a)一个整型数(int a)

b)一个指向整型数的指针(int * a)

c)一个指向指针的指针,它指向的指针是一个指向一个整型数(int **a)

d)一个有10个整型数的数组(int a[10])

e)一个有10个指针的数组,该指针指向一个整型数(int *a[10])

f)一个指向有10个整型数数组的指针(int (*a)[10])

g)一个指向函数的指针,该函数有一个整型参数并返回一个整型树(int (*a)(int))

h)一个有10个指针的数组,该指针是一个指向函数的指针,函数一个整型形参和一个整型的返回值(int (*a[10])(int))

 

19->成员函数通过什么来区分不同对象的成员数据?为什么能够区分?

通过this指针来区别,因为它指向的是对象的首地址。

 

20->拷贝构造函数在哪些情况下会被调用

1)当用类的一个对象去初始化该类的另一个对象时;

2)如果函数的形参是类的对象,调用函数时

3)如果函数的返回值是类的对象,函数调用返回时

 

21->流运算符为什么不能通过类的成员函数重载?一般怎么解决?

通过类的成员函数重载要求运算符的第一个对象是自己,而流运算符的第一个对象是流对象。一般通过友元函数来解决

 

22->虚函数与普通成员函数的区别?内联函数和构造函数是否能为虚函数

区别:虚函数以virtual关键字修饰,有虚指针和虚函数表,虚函数一般只有在运行的时候才能确定调用哪个函数。而普通函数则不然。

内联函数和构造函数不能设置为虚函数。

笔试面试那件小事(常见的C++基础题)