2020年C/C++精选面试题及答案(三)

堆排序的思路

堆是一个完全二叉树
完全二叉树即是:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
堆满足两个性质: 堆的每一个父节点数值都大于(或小于)其子节点,堆的每个左子树和右子树也是一个堆。
堆分为最小堆和最大堆。最大堆就是每个父节点的数值要大于孩子节点,最小堆就是每个父节点的数值要小于孩子节点。排序要求从小到大的话,我们需要建立最大堆,反之建立最小堆。
堆的存储一般用数组来实现。假如父节点的数组下标为i的话,那么其左右节点的下标分别为:(2i+1)和 (2i+2)。如果孩子节点的下标为j的话,那么其父节点的下标为(j-1)/2。
完全二叉树中,假如有n个元素,那么在堆中最后一个父节点位置为(n/2-1)。
算法思想
建立堆
调整堆
交换堆顶元素和堆的最后一个元素
在这里插入图片描述
其他面试题 学习交流群960994558

假如现在有一个数组a[8]={100,33,3,7,11,6,8,5};首先我们要建立完全二叉树。
如下图所示:
在这里插入图片描述

然后根据各个父节点,进行一个划分。如图所示:

在这里插入图片描述

该算法的就是将各个父节点与其自己的孩子结点进行对比,然后交换的过程。根据例子,建立一个最大堆,要求每一个父节点的数值是大于孩子节点的。顺序是从最后一个父节点开始(从左至右,从下至上)。所以第一个父节点是数组下标为3的7,黄色区域中的父节点与孩子节点对比后,发现父节点已经大于孩子节点了,所以不用进行交换了。接下来的顺序就是紫色框中的二叉树,就是数组下标为2的数字3,以数字3为父节点,对比它的孩子节点,从左到右,发现右孩子的数值比父节点大,那么将右节点和父节点进行数值交换。交换后的堆如图所示:

在这里插入图片描述

交换结束后,再看蓝色框中的二叉树,就是以数组下标为1的数字33,以33为父节点,看它的孩子节点是否大于父节点,结果发现不大于,则不用交换。此时这个完全二叉树已经是一个堆。
接下来可以讲堆顶元素和堆的最后一个元素进行互换,然后再降最后一个元素至于完全二叉树外。

在这里插入图片描述

此时完全二叉树中,除去100这个元素之后,这个完全二叉树已经不满足堆的性质,所以要进行调整,此时的每次调整要从根节点(和创建堆不一样)开始进行调整,而且父节点和孩子节点交换后,要追踪到交换的孩子节点上(我用浅蓝色标志的部分)。

在这里插入图片描述

此时33和5交换了位置,那么就要从5为父节点,开始往下再和孩子节点进行比较,此时发现孩子节点和父节点中最大为11,那么将11和父节点(即数字5)互换位置。

在这里插入图片描述

此时堆已经调整好,再将堆顶元素和堆的最后一个元素互换,即将33和3互换,然后再降33至于完全二叉树外。
在这里插入图片描述

此时再进行堆的调整,从根开始。

在这里插入图片描述

在这里插入图片描述

此时调整完堆后,再将堆顶元素和最后一个元素互换位置,即将11和6互换,再将11至于完全二叉树外。

在这里插入图片描述

此时再进行完全二叉树的调整,即就是白色填充的元素进行调整。从根开始,按照上面的方法。

在这里插入图片描述

此时又是一个调整好的堆,将堆顶元素和最后一个元素互换。

在这里插入图片描述

再进行完全二叉树的调整

在这里插入图片描述

此时再进行堆顶元素和最后一个元素的互换
在这里插入图片描述

再进行完全二叉树的调整

在这里插入图片描述

最后进行堆顶元素的互换:

在这里插入图片描述

最后一次调整:

在这里插入图片描述

最后一次堆顶元素交换:

在这里插入图片描述

此时数组已经是一个有序数组,是一个生序数组,用for循环输出即可。

Linux查看文件大小命令.

  1. 使用stat命令查看
    stat命令一般用于查看文件的状态信息。stat命令的输出信息比ls命令的输出信息要更详细。
  2. 使用wc命令
    wc命令一般用于统计文件的信息,比如文本的行数,文件所占的字节数。
  3. 使用du命令
    du命令一般用于统计文件和目录所占用的空间大小。
  4. 使用ls命令
    ls 命令一般用于查看文件和目录的信息,包括文件和目录权限、拥有者、所对应的组、文件大小、修改时间、文件对应的路径等等信息。
  5. 使用ll命令(其实就是ls -l的别名)
    在大部分的Linux系统中,都已经设置了ls -l的别名为ll,所以并不存在ll的命令,ll只是一个别名命令而已。
    Linux使用ll命令查看文件大小

布隆过滤算法

布隆过滤器使用二进制向量结合hash函数来记录任意一条数据是否已经存在于集合中。
布隆过滤器的执行流程为:
首先申请包含SIZE个bit位的Bit集合,并将所有Bit置0。
然后使用数种(k)不同的哈希函数对目标数据进行哈希计算并得到k个哈希值(确保哈希值不超过SIZE大小),然后将Bit集合中以哈希值为下标所处的bit值置为1,由于使用了k个哈希函数,因此记录一条数据的信息将在Bit集合中把k个bit值置为1。
由于哈希函数的稳定性,任意两条相同的数据在Bit集合中所对应的k个bit位置是完全相同的。那么在检测某一条数据是否已经在Bit集合中有记录时,只需检测该条数据的k个哈希值在Bit集合中对应的位置的bit是否均已被标记为1,相反的只要其存在一个哈希值对应的bit位置未被标记为1,则证明该值未被记录过。
使用示例
布隆过滤器的示例如下:
在这里插入图片描述

大体过程为:
首先初始化一个二进制的数组,长度设为 L(图中为 8),同时初始值全为 0 。
当写入一个 A1=1000 的数据时,需要进行 H 次 hash 函数的运算(这里为 2 次);与 HashMap 有点类似,通过算出的 HashCode 与 L 取模后定位到 0、2 处,将该处的值设为 1。
A2=2000 也是同理计算后将 4、7 位置设为 1。
当有一个 B1=1000 需要判断是否存在时,也是做两次 Hash 运算,定位到 0、2 处,此时他们的值都为 1 ,所以认为 B1=1000 存在于集合中。
当有一个 B2=3000 时,也是同理。第一次 Hash 定位到 index=4 时,数组中的值为 1,所以再进行第二次 Hash 运算,结果定位到 index=5 的值为 0,所以认为 B2=3000 不存在于集合中。
优缺点
优点
时间复杂度为O(n),且布隆过滤器不需要存储元素本身,使用位阵列,占用空间也很小。
缺点
通过布隆过滤,我们能够准确判断一个数不存在于某个集合中,但对于存在于集合中这个结论,布隆过滤会有误报(可能存在两组不同数据但其多个哈希值完全一样的情况)。但是通过控制Bit集合的大小(即SIZE)以及哈希函数的个数,可以将出现冲突的概率控制在极小的范围内,或者通过额外建立白名单的方式彻底解决哈希冲突问题。
误判率计算公式为(1 – e(-nk/SIZE))k,其中n为目标数据的数量,SIZE为Bit集合大小,k为使用的哈希函数个数;假设现有一千万条待处理数据,Bit集合大小为2^30(约10亿,即占用内存128MB),使用9个不同的哈希函数,计算可得任意两条数据其9次哈希得到的哈希值均相同(不考虑顺序)的概率为2.6e-10,约为38亿分之一。

给定一组非负整数组成的数组h,代表一组柱状图的高度,其中每个柱子的宽度都为1。 在这组柱状图中找到能组成的最大矩形的面积。 入参h为一个整型数组,代表每个柱子的高度,返回面积的值。 输入描述:

输入包括两行,第一行包含一个整数n(1 ≤ n ≤ 10000)
第二行包括n个整数,表示h数组中的每个值,h_i(1 ≤ h_i ≤ 1,000,000)
输出描述:
输出一个整数,表示最大的矩阵面积。

输入例子1:
6
2 1 5 6 2 3

输出例子1:
10

#include <bits/stdc++.h>
using namespace std;

const int maxn=10000+5;
typedef long long ll;
int h[maxn],l[maxn],r[maxn];

int main()
{
//freopen(“in.txt”,“r”,stdin);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
l[1]=0;
for(int i=2;i<=n;i++)
{
int lp=i-1;
while(h[lp]>=h[i])
lp=l[lp];
l[i]=lp;
}
r[n]=n+1;
for(int i=n-1;i>=1;i–)
{
int hp=i+1;
while(h[hp]>=h[i])
hp=r[hp];
r[i]=hp;
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,(ll)h[i]*(r[i]-l[i]-1));
printf("%lld\n",ans);
return 0;
}

一个C++源文件从文本到可执行文件经历的过程?

对于C++源文件,从文本到可执行文件一般需要四个过程:
预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。
编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件
汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件
链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件
请问malloc的原理,brk系统调用和mmap系统调用的作用分别是什么?
malloc函数用于动态分配内存。为了减少内存碎片和系统调用的开销,malloc其采用内存池的方式,先申请大块内存作为堆区,然后将堆区分为多个内存块,以块作为内存管理的基本单位。当用户申请内存时,直接从堆区分配一块合适的空闲块。malloc采用隐式链表结构将堆区分成连续的、大小不一的块,包含已分配块和未分配块;同时malloc采用显示链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个空闲块记录了一个连续的、未分配的地址。
当进行内存分配时,malloc会通过隐式链表遍历所有的空闲块,选择满足要求的块进行分配;当进行内存合并时,malloc采用边界标记法,根据每个块的前后块是否已经分配来决定是否进行块合并。
malloc在申请内存时,一般会通过brk或者mmap系统调用进行申请。其中当申请内存小于128K时,会使用系统函数brk在堆区中分配;而当申请内存大于128K时,会使用系统函数mmap在映射区分配。

请问共享内存相关api?

Linux允许不同进程访问同一个逻辑内存,提供了一组API,头文件在sys/shm.h中。
1)新建共享内存shmget
int shmget(key_t key,size_t size,int shmflg);
key:共享内存键值,可以理解为共享内存的唯一性标记。
size:共享内存大小
shmflag:创建进程和其他进程的读写权限标识。
返回值:相应的共享内存标识符,失败返回-1
2)连接共享内存到当前进程的地址空间shmat
void *shmat(int shm_id,const void *shm_addr,int shmflg);
shm_id:共享内存标识符
shm_addr:指定共享内存连接到当前进程的地址,通常为0,表示由系统来选择。
shmflg:标志位
返回值:指向共享内存第一个字节的指针,失败返回-1
3)当前进程分离共享内存shmdt
int shmdt(const void *shmaddr);
4)控制共享内存shmctl
和信号量的semctl函数类似,控制共享内存
int shmctl(int shm_id,int command,struct shmid_ds *buf);
shm_id:共享内存标识符
command: 有三个值
IPC_STAT:获取共享内存的状态,把共享内存的shmid_ds结构复制到buf中。
IPC_SET:设置共享内存的状态,把buf复制到共享内存的shmid_ds结构。
IPC_RMID:删除共享内存
buf:共享内存管理结构体。

请问reactor模型组成.

reactor模型要求主线程只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程,除此之外,主线程不做任何其他实质性的工作,读写数据、接受新的连接以及处理客户请求均在工作线程中完成。其模型组成如下:
在这里插入图片描述

1)Handle:即操作系统中的句柄,是对资源在操作系统层面上的一种抽象,它可以是打开的文件、一个连接(Socket)、Timer等。由于Reactor模式一般使用在网络编程中,因而这里一般指Socket Handle,即一个网络连接。
2)Synchronous Event Demultiplexer(同步事件复用器):阻塞等待一系列的Handle中的事件到来,如果阻塞等待返回,即表示在返回的Handle中可以不阻塞的执行返回的事件类型。这个模块一般使用操作系统的select来实现。
3)Initiation Dispatcher:用于管理Event Handler,即EventHandler的容器,用以注册、移除EventHandler等;另外,它还作为Reactor模式的入口调用Synchronous Event Demultiplexer的select方法以阻塞等待事件返回,当阻塞等待返回时,根据事件发生的Handle将其分发给对应的Event Handler处理,即回调EventHandler中的handle_event()方法。
4)Event Handler:定义事件处理方法:handle_event(),以供InitiationDispatcher回调使用。
5)Concrete Event Handler:事件EventHandler接口,实现特定事件处理逻辑。

请问Linux虚拟地址空间

为了防止不同进程同一时刻在物理内存中运行而对物理内存的争夺和践踏,采用了虚拟内存。
虚拟内存技术使得不同进程在运行过程中,它所看到的是自己独自占有了当前系统的4G内存。所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。 事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。
虚拟内存的好处:
1.扩大地址空间;
2.内存保护:每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。虚存还对特定的内存地址提供写保护,可以防止代码或数据被恶意篡改。
3.公平内存分配。采用了虚存之后,每个进程都相当于有同样大小的虚存空间。
4.当进程通信时,可采用虚存共享的方式实现。
5.当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存
6.虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。在内存中可以保留多个进程,系统并发度提高
7.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片

虚拟内存的代价:

1.虚存的管理需要建立很多数据结构,这些数据结构要占用额外的内存
2.虚拟地址到物理地址的转换,增加了指令的执行时间。
3.页面的换入换出需要磁盘I/O,这是很耗时的
4.如果一页中只有一部分数据,会浪费内存。

请问操作系统中的缺页中断

malloc()和mmap()等内存分配函数,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常。
缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存是,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:
1、保护CPU现场
2、分析中断原因
3、转入缺页中断处理程序进行处理
4、恢复CPU现场,继续执行
但是缺页中断是由于所要访问的页面不存在于内存时,由硬件所产生的一种特殊的中断,因此,与一般的中断存在区别:
1、在指令执行期间产生和处理缺页中断信号
2、一条指令在执行期间,可能产生多次缺页中断
3、缺页中断返回是,执行产生中断的一条指令,而一般的中断返回是,执行下一条指令。
【点击加入群聊学习交流群】

doc格式,60多页吧,几百道题吧,都有答案吧,看好在下!部分:1.求下面函的返回值(微软)int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定x = 9999。 答案:8思路:将x转化为2进制,看含有的1的个。2. 什么是“引用”?申明和使用“引用”要注意哪些问题?答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立组的引用。3. 将“引用”作为函有哪些特点?(1)传递引用给函与传递指针的效果是一样的。这时,被调函的形参就成为原来主调函中的实参变量或对象的一个别名来使用,所以在被调函中对形参变量的操作就是对其相应的目标对象(在主调函中)的操作。(2)使用引用传递函的参,在内存中没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函的参,当发生函调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函。因此,当参传递的据较大时,用引用比用一般变量传递参的效率和所占空间都好。(3)使用指针作为函的参虽然也能达到与使用引用的效果,但是,在被调函中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。4. 在什么时候需要使用“常引用”? 如果既要利用引用提高程序的效率,又要保护传递给函据不在函中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;例1int a ;const int &ra=a;ra=1; //错误a=1; //正确 例2string foo( );void bar(string & s); 那么下面的表达式将是非法的:bar(foo( ));bar("hello world"); 原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参应该在能被定义为const的情况下,尽量定义为const 。5. 将“引用”作为函返回值类型的格式、好处和需要遵守的规则?格式:类型标识符 &函名(形参列表及类型说明){ //函体 }好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!注意事项:(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。 (2)不能返回函内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函内部new分配内存的引用),又面临其它尴尬局面。例如,被函返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。(4)流操作符重载返回值申明为“引用”的作用:流操作符<>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。例3#i nclude int &put(int n);int vals[10];int error=-1;void main(){put(0)=10; //以put(0)值作为左值,等价于vals[0]=10; put(9)=20; //以put(9)值作为左值,等价于vals[9]=20; cout<<vals[0]; cout<<vals[9];} int &put(int n){if (n>=0 && n<=9 ) return vals[n]; else { cout<<"subscript error"; return error; }} (5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。6. “引用”与多态的关系?引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。例4Class A; Class B : Class A{...}; B b; A& ref = b;7. “引用”与指针的区别是什么?指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函传ref和pointer的区别。8. 什么时候需要“引用”?流操作符<>、赋值操作符=的返回值、拷贝构造函的参、赋值操作符=的参、其它情况都推荐使用引用。以上 2-8 参考:http://blog.youkuaiyun.com/wfwd/archive/2006/05/30/763551.aspx9. 结构与联合有和区别?1. 结构和联合都是由多个不同的据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。10. 下面关于“联合”的题目输出?a)#i nclude union{int i;char x[2];}a;void main(){a.x[0] = 10; a.x[1] = 1;printf("%d",a.i);}答案:266 (低地址,高高地址,内存占用情况是Ox010A)………………
1.static有什么用途?(请至少说明两种) 1)在函体,一个被声明为静态的变量在这一函被调用过程中维持其值不变。 2) 在模块内(但在函体外),一个被声明为静态的变量可以被模块内所用函访问,但不能被模块外其它函访问。它是一个本地的全局变量。 3) 在模块内,一个被声明为静态的函只可被这一模块内的其它函调用。那就是,这个函被限制在声明它的模块的本地范围内使用 2.引用与指针有什么区别? 1) 引用必须被初始化,指针不必。 2) 引用初始化以后不能被改变,指针可以改变所指的对象。 3) 不存在指向空值的引用,但是存在指向空值的指针。 3.描述实时系统的基本特性 在特定时间内完成特定的任务,实时性与可靠性。 4.全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 全局变量储存在静态据库,局部变量在堆栈。 5.什么是平衡二叉树? 左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。 7.什么函不能声明为虚函? constructor函不能声明为虚函。 8.冒泡序算法的时间复杂度是什么? 时间复杂度是O(n^2)。 9.写出float x 与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001) 10.Internet采用哪种网络协议?该协议的主要层次结构? Tcp/Ip协议 主要层次结构为: 应用层/传输层/网络层/据链路层/物理层。 11.Internet物理地址和IP地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析協議) 12.IP地址的编码分为哪俩部分? IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按与上之后才能区分哪些是网络哪些是主机13.用户输入M,N值,从1至N开始顺序循环,每到M输出值,直至全部输出。写出C程序。 循环链表,用取余操作做 14.不能做switch()的参类型是: switch的参不能为实型。 1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分) int a = 4; (A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++); a = ? 答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a; 改后答案依次为9,10,10,11 2.某32系统下, C++程序,请计算sizeof 的值(5分). char str[] = “http://www.ibegroup.com/” char *p = str ; int n = 10; 请计算 sizeof (str ) = ?(1) sizeof ( p ) = ?(2) sizeof ( n ) = ?(3) void Foo ( char str[100]){ 请计算 sizeof( str ) = ?(4) } void *p = malloc( 100 ); 请计算 sizeof ( p ) = ?(5) 答:(117 (2)4 (3) 4 (4)4 (5)4 3. 回答下面的问题. (4分) (1).头文件中的 ifndef/define/endif 干什么用?预处理 答:防止头文件被重复引用 (2). #i nclude 和 #i nclude “filename.h” 有什么区别? 答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。 (3).在C++ 程序中调用被 C 编译器编译后的函,为什么要加 extern “C”声明? 答:函和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变 量和函是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调 用C 函C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。 (4). switch()中不允许的据类型是? 答:实型 4. 回答下面的问题(6分) (1).Void GetMemory(char **p, int num){ *p = (char *)malloc(num); } void Test(void){ char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 请问运行Test 函会有什么样的结果? 答:输出“hello” (2). void Test(void){ char *str = (char *) malloc(100); strcpy(str, “hello”); free(str); if(str != NULL){ strcpy(str, “world”); printf(str); } } 请问运行Test 函会有什么样的结果? 答:输出“world” (3). char *GetMemory(void){ char p[] = "hello world"; return p; } void Test(void){ char *str = NULL; str = GetMemory(); printf(str); } 请问运行Test 函会有什么样的结果? 答:无效的指针,输出不确定 5. 编写strcat函(6分) 已知strcat函的原型是char *strcat (char *strDest, const char *strSrc); 其中strDest 是目的字符串,strSrc 是源字符串。 (1)不调用C++/C 的字符串库函,请编写函 strcat 答: VC源码: char * __cdecl strcat (char * dst, const char * src) { char * cp = dst; while( *cp ) cp++; /* find end of dst */ while( *cp++ = *src++ ) ; /* Copy src to end of dst */ return( dst ); /* return dst */ } (2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值? 答:方便赋值给其他变量 6.MFC中CString是类型安全类么? 答:不是,其它据类型转换到CString可以使用CString的成员函Format来转换 7.C++中为什么用模板类。 答:(1)可用来创建动态增长和减小的据结构 (2)它是类型无关的,因此具有很高的可复用性。 (3)它在编译时而不是运行时检查据类型,保证了类型安全 (4)它是平台无关的,可移植性 (5)可用于基本据类型 8.CSingleLock是干什么的。 答:同步多个线程对一个据类的同时访问 9.NEWTEXTMETRIC 是什么。 答:物理字体结构,用来设置字体的高宽大小 10.程序什么时候应该使用线程,什么时候单线程效率高。 答:1.耗时的操作使用线程,提高应用程序响应 2.行操作时使用线程,如C/S架构的服务器端发线程响应用户的请求。 3.多CPU系统中,使用线程提高CPU利用率 4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独 立的运行部分,这样的程序会利于理解和修改。 其他情况都使用单线程。 11.Windows是内核级线程么。 答:见下一题 12.Linux有内核级线程么。 答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两 种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序 中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一 种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部 需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支 ,且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线 程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不 到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的发优势,但却占 用了更多的系统开支。 Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程 13.C++中什么据分配在栈或堆中,New分配据是在近堆还是远堆中? 答:栈: 存放局部变量,函调用参,函返回值,函返回地址。由系统管理 堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上 14.使用线程是如何防止出现大的波峰。 答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提 高调度效率和限制资源使用的好处,线程池中的线程达到最大时,其他线程就会队 等候。 15函模板与类模板有什么区别? 答:函模板的实例化是由编译程序在处理函调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。 16一般据库若出现日志满了,会出现什么情况,是否还能使用? 答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记 录日志。也就是说基本上处于不能使用的状态。 17 SQL Server是否支持行级锁,有什么好处? 答:支持,设立封锁机制主要是为了对发操作进行控制,对干扰进行封锁,保证据 的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不 被其它用户所修改。因而行级锁即可保证据的一致性又能提高据操作的迸发性。 18如果据库满了会出现什么情况,是否还能使用? 答:见16 19 关于内存对齐的问题以及sizof()输出 答:编译器自动对齐的原因:为了提高程序的性能,据结构(尤其是栈)应该尽可能 地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问 ;然而,对齐的内存访问仅需要一次访问。 20 int i=10, j=10, k=3; k*=i+j; k最后的值是? 答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低 21.对据库的一张表进行操作,同时要对另一张表进行操作,如何实现? 答:将操作多个表的操作放入到事务中进行处理 22.TCP/IP 建立连接的过程?(3-way shake) 答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用次握手建立一个连接。   第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,进入SYN_SEND状 态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个 SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;   第次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1) ,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成次握手。 23.ICMP是什么协议,处于哪一层? 答:Internet控制报文协议,处于网络层(IP层) 24.触发器怎么工作的? 答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT 、 DELETE 这些操作时,据库就会自动执行触发器所定义的SQL 语句,从而确保对 据的处理必须符合由这些SQL 语句所定义的规则。 25.winsock建立连接的主要实现步骤? 答:服务器端:socker()建立套接字,绑定(bind)监听(listen),用accept() 等待客户端连接。 客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv( ),在套接字上写读据,直至据交换完毕,closesocket()关闭套接字。 服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连 接。该新产生的套接字使用send()和recv()写读据,直至据交换完毕,closesock et()关闭套接字。 26.动态连接库的两种方式? 答:调用一个DLL中的函有两种方法: 1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函 ,使得他们就像本地函一样。这需要链接时链接那些函所在DLL的导入库,导入库向 系统提供了载入DLL时所需的信息及DLL函。 2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa dLibraryEx函载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函的 出口地址,然后就可以通过返回的函指针调用DLL函了。如此即可避免导入库文件了 。 27.IP组播有那些好处? 答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧 消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的据包 到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无 论有多少个目标地址,在整个网络的任何一条链路上只传送单一的据包。所以说组播 技术的核心就是针对如何节约网络资源的前提下保证服务质量。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值