面试题大集合

来源:互联网 发布:什么叫app端口 编辑:程序博客网 时间:2024/05/16 16:02

C++面试参考1
·指针篇

1.写一个数组指针与指针数组,并说出齐区别。

2.函数指针与指针函数的区别。

3.指针与引用的区别和相同点。

4.指针的&和*

5.函数的传值和传值区别

6.理解一个指针的指针和一个指向数组指针的指针

7.空指针能赋值吗?

8.”()”,”[]”,”*”的优先级。

9.理解数组指针和指针数组的内存分布。

10.给出结果:

struct Test{   int Num;   char *pcName;   short sDate;   char cha[2];   short sBa[4];}*p;p + 0x1 = 0x___ ?(unsigned long)p + 0x1 = 0x___?(unsigned int*)p + 0x1 = 0x___?

11.写出一个野指针
由于free(),是把指针所指向的内存地址位置的一块内存上面的数据释放了,但是指针中的值还是指向该内存块首地址,这样就导致在if语句中判断是不等于NULL是合法的,所以任然可以使用

    char* str_1 = (char*)malloc(100);    strcpy(str_1,"hello");    free(str_1);    if(str_1 != NULL) {        //指针指向的内存上面的值已经被清除        printf(" >>>> strr = [%s]\n",str_1);        strcpy(str_1,"world");        printf(" >>>> strr = [%s]\n",str_1);    }结果: >>>> strr = [葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺] >>>> strr = [world]

12.int i=10, j=10, k=3; k*=i+j; k最后的值是?

答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低


常用类型名称篇
知识点1:

  • auto,register,extern,static,对应两种存储期:自动存储期和静态存储期。
    auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。

1.static的用途。

  • 特点: static局部变量的”记忆性”与生存期的”全局性”

  • 对于函数来说,具有隐藏功能,也就是该函数只针对当前声明的源文件有效,也就不会被其他文件访问和修改,同时如果其他文件中存在相同名称,不会引起冲突,移植性更强

  • 保存变量内容的持久性,我们知道存储在静态区的变量有全局变量和static变量,静态区在程序运行时完成吃时候

  • 默认初始化为0,我们可以将一个变量声明为static,特别是比较大的变量时,这样可以减少程序运行中的工作量

  • 当static修饰的是局部变量时,则改变了就不是存在栈中了,而是在静态区,且当局部变量在离开作用域后,变量并没被释放,任然驻留在内存中,直到程序结束;可以设置变量的存储域

  • 统计计数功能:声明函数的一个局部变量,并设为static类型,作为一个计数器,这样函数每次被调用的时候就可以进行计数。这是统计函数被调用次数的最好的办法,因为这个变量是和函数息息相关的,而函数可能在多个不同的地方被调用,所以从调用者的角度来统计比较困难。

2.const的用途。
参考1
参考2

  • const只会一个修饰符,被修饰的对象将意味着“只读”,const修饰的可以是变量,但不意味着常量, const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效(如:const char a == char const *a != char const a)
const int a; int const a; //以上表示都是一样的,表示整形数a只读,一般在设置一个数为const时都要初始化,因为后面就没机会了const int *a;  //const修饰的是一个整形指针,所以其指向的值不能改变int * const a;  //const修饰的是指针a本身的地址,所以指针a不能修改,也就是不能改变a中的值int const * a const;  //const及修饰整形指针也修饰指针本身

作用:
* 防止一个变量被修改,在声明时记得初始化值
* 可以设置指针本身为只读属性,也可以设置指针所指向的值只读属性
* 在函数声明中,修饰形参,表示它是一个输入形参,在函数内不可被修改
* 在C++中,修饰成员函数,使其为常函数,不能被修改
* 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:
const classA operator*(const classA& a1,const classA& a2);
  operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:
classA a, b, c;
(a * b) = c; // 对a*b的结果赋值
  操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。
* 节省空间,避免不必要的内存分配
* 提高效率,编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

3.使用的extern “C”和extern “C++”的意义。

由于C和C++之间存在差异,由于C++具有多态性,也就是相同的函数名可以完成不同的功能,CPP 通常是通过参数区分具体调用的是哪一个函数。在编译的时候,CPP 编译器会将参数类型和函数名连接在一起,于是在程序编译成为目标文件以后,CPP 编译器可以直接根据目标文件中的符号名将多个目标文件连接成一个目标文件或者可执行文件。但是在C 语言中,由于完全没有多态性的概念,C 编译器在编译时除了会在函数名前面添加一个下划线之外,什么也不会做(至少很多编译器都是这样干的)。由于这种的原因,当采用CPP 与C 混合编程的时候,就可能会出问题。假设在某一个头文件中定义了这样一个函数:

int foo(int a, int b);

而这个函数的实现位于一个.c 文件中,同时,在.cpp 文件中调用了这个函数。那么,当CPP编译器编译这个函数的时候,就有可能会把这个函数名改成_fooii ,这里的ii 表示函数的第一参数和第二参数都是整型。而C 编译器却有可能将这个函数名编译成_foo 。也就是说,在CPP 编译器得到的目标文件中,foo() 函数是由_fooii 符号来引用的,而在C 编译器生成的目标文件中,foo() 函数是由_foo指代的。但连接器工作的时候,它可不管上层采用的是什么语言,它只认目标文件中的符号。于是,连接器将会发现在.cpp 中调用了foo() 函数,但是在其它的目标文件中却找不到_fooii 这个符号,于是提示连接过程出错。extern “C” {} 这种语法形式就是用来解决这个问题的。本文将以示例对这个问题进行说明。

注意:用g++编译cpp程序时,编译器会定义宏 __cplusplus ,可根据__cplusplus是否定义决定是否需要extern “C”。
总结:由于C++与C的在编译时,使用的编译器不同C(gcc),C++(g++),所以编译的协议可以有所不同,所以如果C++在编译C的接口时,不是按照C编译器的规则的话,则到时候在连接器中就无法连接,很好的例子就是C++使用C写的dll时,所以可以说是解决C和C++的兼容
总结:extern “C”使用来混合使用C/C++编程时用来解决C/C++的兼容性的,进一步说就是告诉连接器在目标文件中寻找对应接口时,以C还是C++的方式来寻找和连接

4.extern和export的意义。

extern:在定义函数时,如果在函数首部的最左端冠以关键字extern,
则表示此函数是外部函数,可供其它文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。

export:export关键字只有在使用局部编译模式时才会用到,同时需要编译器支持局部编译模式,如果编译器不支持这种编译模式,那么不能使用export

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

ifndef和endif是一条条件预编译宏,ifndef表示如果没有定义特定宏,则执行,如果定义了该特定宏则不执行
endif用来结束条件预编译宏
define用来声明一个预编译宏

6.#include<>和#include”“的区别

在寻找包含的头文件时,搜索的环境域不一样
<>:环境域不仅仅包含源文件目录,同时也包含用户设置的环境
“”:则只包含源文件目录


基本知识了解
1.描述实时系统的基本特性。

资源可以随时动态分配,处理能力强,速度较快
1)高精度计时系统
计时精度是影响实时性的一个重要因素。在实时应用系统中,经常需要精确确定实时地操作某个设备或执行某个任务,或精确的计算一个时间函数。这些不仅依赖于一些硬件提供的时钟精度,也依赖于实时操作系统实现的高精度计时功能。
2)多级中断机制
一个实时应用系统通常需要处理多种外部信息或事件,但处理的紧迫程度有轻重缓急之分。有的必须立即作出反应,有的则可以延后处理。因此,需要建立多级中断嵌套处理机制,以确保对紧迫程度较高的实时事件进行及时响应和处理。
3)实时调度机制
实时操作系统不仅要及时响应实时事件中断,同时也要及时调度运行实时任务。但是,处理机调度并不能随心所欲的进行,因为涉及到两个进程之间的切换,只能在确保“安全切换”的时间点上进行,实时调度机制包括两个方面,一是在调度策略和算法上保证优先调度实时任务;二是建立更多“安全切换”时间点,保证及时调度实时任务。

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

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

内存堆栈篇
1.全局变量与局部变量在内存中是否有区别。
参考

变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。
按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。
按作用域分,全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。
全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。

2.堆栈溢出一般是由于什么原因导致的。

1).没有回收垃圾资源
2).层次太深的递归调用
3).内存泄露,比如某一数组原先已定义好大小,但是在后续操作中存放的个数超出这一既定长度,会导致堆栈溢出
4).由于程序员动态申请的内存块使用后未立即释放,导致内存区不够用,也会导致堆栈溢出
5).程序陷入死循环,往内存写数据,不断地消耗内存空间
6).程序本身运行起来就要消耗一定大小的内存,但是系统提供的实际内存不够,比如JVM虚拟内存不够让程序使用引用块内容

3.内存的分配方式以及它们的区别。

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

4.用两个栈实现一个队列的功能?要求给出算法和思路!

设2个栈为A,B, 一开始均为空.
入队:
将新元素push入栈A; 出队:
(1)判断栈B是否为空;
(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;
(3)将栈B的栈顶元素pop出;

5.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?

答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理 堆: 程序运行时动态申请,new
和 malloc申请的内存就在堆上

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

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

算法篇

1.什么是平衡二叉树。

它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。常用算法有红黑树、AVL、Treap、伸展树等。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log2n),大大降低了操作的时间复杂度。

2.冒泡排序算法的时间复杂度是什么。

O(n^2),可以通过程序来验证
小于10000个数据的数组用它不会超时(大概一秒)
但如果更大就要用快排或归并O(n*log2(n))

3.写一个冒泡排序

#include<stdio.h>#include<stdlib.h>#include<string.h>void Sort(unsigned char* data,unsigned int size){    int i=0,j=0;    for(i=0;i<size;i++){        for(j=0;j < size -1;j++){            if(*(data+j) > *(data + j + 1)) {                char c = *(data + j + 1);                 *(data + j + 1) = *(data + j);                 *(data + j) = c;            }           }       }   }void main(){    char data[] = "54321sdf234";    int size = sizeof(data);    printf("size= [%d]\n",size);    Sort(data,size-1);    int i=0;    for(i=0;i<size-1;i++){        printf("data[%d] = [%c]\n",i,*(data+i));    }   }

数据结构篇
1.什么函数不能声明为虚函数。

虚函数定义:
虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,在基类的类定义中定义虚函数的一般形式:
  virtual 函数返回值类型 虚函数名(形参表)  { 函数体 }
虚函数作用:
虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型,以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。
当程序发现虚函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。
设置虚函数须注意:
1):只有类的成员函数才能说明为虚函数;
2):静态成员函数不能是虚函数;
3):内成员为内联函数不能为虚函数;
4):构造函数不能是虚函数;
5):析构函数可以是虚函数,而且通常声明为虚函数。

2.什么是内联(inline)和联合

inline(小心,不是online),翻译成“内联”或“内嵌”。意指:当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。这样做的不好处:由于每当代码调用到内联函数,就需要在调用处直接插入一段该函数的代码,所以程序的体积将增大。
“联合”是一种特殊的类,也是一种构造类型的数据结构。 在一个“联合”内可以定义多种不同的数据类型, 一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。

3.不能做switch()的参数类型是:

switch参数类型:switch 后面的表达式不能跟double,float,long,String ,boolean,可以接int,short,byte,enum!

4.MFC中CString是类型安全类吗。

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

5.C++中为什么用模板类。

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

6.程序什么时候应该使用线程,什么时候单线程效率高。

1).耗时的操作使用线程,提高应用程序响应
2).并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
3).多CPU系统中,使用线程提高CPU利用率
4).改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。 其他情况都使用单线程。

7.Linux有内核级线程么以及Linux有内核级线程么。

线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似Windows 3.x 的协作式多任务。另外一种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。
Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程

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

答:调用一个DLL中的函数有两种方法:
1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数 ,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。
2.运行时动态链接(run-time dynamiclinking),运行时可以通过LoadLibrary或LoadLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了 。

代码编写篇

1.写一个float x 与零值的比较的if语句;
这里主要存在一个float精度的问题,尽量少用“==”或者“!=”来比较float/double

#define FLT 1e-6float x=0.00003;if((x >= FLT)) {    //x大于0} else if( x >= (-FLT) || x <= FLT ) {    //x = 0} else {    //x < 0}

2.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直到全部输出为止,写出C程序

编程思路:首先建立一个循环指针,用来对1~N进行循环,然后就是每次数到M时,delete当前指针,从头再一次循环,直到循环指针的数量少于M为止

3.判断自加运算正确与否

了解左值和右值:C/C++语言中可以放在赋值符号左边的变量,左值表示存储在计算机内存的对象,左值相当于地址值。右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的“右值”,也就是其代表的真实值,右值相当于数据值
优先级: () > ++ > +

int a = 4,b = 4,c = 4,d = 4;a += (a++);b += (++b);(c++) += c; //左值不能自加(d++) += (d++);//左值不能自加结果:plusplus.c: In function ‘main’:plusplus.c:17:8: error: lvalue required as left operand of assignment  (c++) += c;        ^plusplus.c:18:8: error: lvalue required as left operand of assignment  (d++) += (d++);a = [9]b = [10]c = [4]d = [4]在C语言中,++c = 10;++c=10;++(c=10);都是非法的然而在C++中则要另提别论了,要注意C中自增自减都不能作为左值

4.计算sizeof

    void Foo(char str100[100]){        printf("sizeof(str100) =[%d]\n",sizeof(str100));    }    char str[] = "563200085@qq.com";    char* p = str;    int n = 10;     printf("sizeof(str) = [%d]\n",sizeof(str));    printf("sizeof(p) = [%d]\n",sizeof(p));    printf("sizeof(n) = [%d]\n",sizeof(n));    Foo(str);    void* vp = (void*)malloc(100);    printf("sizeof(vp) = [%d]\n",sizeof(vp));结果:sizeof(str) = [17]sizeof(p) = [4]sizeof(n) = [4]sizeof(str100) = [4]sizeof(vp) = [4]经检测,在C和C++中都是一样的

5.不用C++/C的字符串库,编写strcat

//原型:char* strcat(char *strDst,char *strSrc);char* CatStr(char* dst,char src){    if(!dst || !src) return NULL;    char* p = dst;    while(*dst){        dst++;    }    while((*dst++ = *src++));    return p;}

网络及协议篇
1.Internet采用哪种网络协议?该协议的主要层次。

TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议,又叫网络通讯协议,这个协议是Internet国际互联网络的基础
主要层次:数据链路层、网络层、传输层、应用层
数据链路层: 这是TCP/IP软件的最低层,负责接收IP数据报并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层。
网络层: 负责相邻计算机之间的通信。其功能包括三方面:
1、处理来自传输层的分组发送请求,收到请求后,将分组装入IP数据报,填充报头,选择去往信宿机的路径,然后将数据报发往适当的网络接口。
2、处理输入数据报:首先检查其合法性,然后进行寻径–假如该数据报已到达信宿机,则去掉报头,将剩下部分交给适当的传输协议;假如该数据报尚未到达信宿,则转发该数据报。
3、处理路径、流控、拥塞等问题。
传输层:
1、格式化信息流;
2、提供可靠传输。为实现后者,传输层协议规定接收端必须发回确认,并且假如分组丢失,必须重新发送。
应用层:向用户提供一组常用的应用程序,比如电子邮件、文件传输访问、远程登录等。远程登录TELNET使用TELNET协议提供在网络其它主机上注册的接口。TELNET会话提供了基于字符的虚拟终端。文件传输访问FTP使用FTP协议来提供网络内机器间的文件拷贝功能。

2.Internet物联地址和IP地址转换采用什么协议。

ARP协议(Address Resolution Protocol)

3.IP地址的编码分为那两部分。

从理论上来讲IP地址分为两个部分:网络ID+主机ID。网络ID就是网络地址,它是供路由器在路由表中寻址使用的。主机ID就是在局域网中的主机号;
网络ID的确定有两种方式:在有类网络中直接使用IP分类来确定,A类的8位,B类的16位,C类的24位。在无类路由中使用IP地址与掩码计算得到网络ID。
如:
如果要知道网络号,主机号,光有IP地址还不行,还需要知道掩码。 假设你的掩码是255.255.255.0

1.转换192.168.5.125到2进制为11000000.10101000.00000101.01111101
2.转换255.255.255.0到2进制为11111111.11111111.11111111.00000000 每段8位,不足8位的,前面加0补齐
3.把IP和子网掩码的每位数AND。
11000000.10101000.00000101.01111101
11111111.11111111.11111111.00000000 and
*————————————-
11000000.10101000.00000101.00000000 转换10进制=192 .168 .5 .0 得到的数值就是网络标示(网络号)
4.将掩码取反运算为00000000.00000000.00000000.11111111
5.与IP地址and运算
11000000.10101000.00000101.01111101
00000000.00000000.00000000.11111111 and
*—————————————-
00000000.00000000.00000000.01111101 10进制 = 0. 0. 0.125 得到的数值就是主机标示(主机号)

4.ICMP是什么协议,处于哪一层?

答:Internet控制报文协议,处于网络层(IP层)

5.winsock建立连接的主要实现步骤?

服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept() 等待客户端连接。
客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(
),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock et()关闭套接字。

5.IP组播有那些好处?

答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播 技术的核心就是针对如何节约网络资源的前提下保证服务质量

线程与进程

1.使用线程是如何防止出现大的波峰。

答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队 等候。

1 0