6.2 笔试真题 & 详解
真题 1:
1,ISR,APC,DPC 的定义。
2,Windows/ Linux 下系统创建进程的步骤。
3,xtoa 函数,输入是两个整数,原数字,数制,输出是字符串。
4,实现内存拷贝的 C 函数。
5,比较两个二叉树结构是否一样。
6,C++中 volatile 关键字的作用。
7,一个程序找错误。
8,根据上述错误给程序员发邮件提出改进意见。
9,最喜欢的一首唐诗,原因。
10,当代最崇拜的人,原因。
真题 2:
1、选择下面哪一个是时序电路。四个选项分别是 ADD XOR Latch D-Flop。
2、问系统工作的最大时钟频率是否跟 Hold time 有关,并说明理由。
3、画出下面两个代码综合出来的电路图,并说明原因。 a:if(aflag)
begin opt1 <= a; opt2 <= b; end esle begin opt1 <= c; opt2 <= d; end
always@(posedge clk)
out_data <= opt1 +opt2; b: if(aflag)
out_data <= a+b; else
out_data <= c+d;
4、用图表说明下列脚本命令。
a: set_multicycle_path 4 -setup -from dffa/cp -to dffb/d b: set_multicycle_path 4 -setup -from dffa/cp -to dffb/d set_multicycle_path 4 -hold -from dffa/cp -to dffb/d
//这三个命令不一定完全正确,凭记忆大概是这个样子的
5、5 分频,用 Verilog HDL/VHDL 实现。
6、timescale 1ns/1ps 与`timescale 1ns/50ps 分别代表的意义以及区别。
7.分别用 shell 以及 perl(或任一你熟知的脚本语言)将当前目录下所有".c"文件后缀改 成".cc"。
真题 3:
1:windows API 里面用于线程同步的有哪些?
答案:共有 12 个 API
1)、临界区共有五个 API。
(1)InitializeCriticalSection
此函数用于设臵临界区对象, 即对临界区对象初始化。该函数必须在执行 EnterCriticalSection 前调用。单个进程的线程可以为互斥同步使用临界区对象。进程负 责分配临界区对象使用的内存,可以通过对 CRITICAL_SECTION 类型变量的定义和使用 来实现。
(2)EnterCriticalSection
此函数用于等待指定的临界区对象的所有权。授予调用线程所有权后,该函数返回, 临界区对象在单个进程的各线程内强制互斥同步。在线程拥有临界区对象以后,对同一 个临界区对象应调用
nterCriticalSection 函数,防止发生死锁。在退出临界区后用 LeaveCriticalSection 函数使其他线程可以进入临界区。
(3)TryEnterCriticalSection
此函数用没有阻塞的方式试图进入一个临界区。若函数调用成功,则进行调用的线 程拥有对临界区的所有权,否则立即返回。
(4)LeaveCriticalSection
此函数用于释放对临 界区对象的所有权。 每次线程对同一个对象执行 EnterCriticalSection 或 TryEnterCriticalSection 都必须调用 LeaveCriticalSection 函数。
(5)DeleteCriticalSection 此函数用于删除一个临界区对象,释放所有与不再为自己所控制的临界区对象有关的资源。一个临界区对象被删除, 就不能再对其调用函数 EnterCriticalSection ,函数TryEnterCriticalSection 和函数 LeaveCriticalSection 了。
2)、互斥和信号量共有 7 个 API。
(1)CreateMutex
此函数用于创建命名或未命名的互斥对象。这些互斥对象用于进程同步,当互斥对 象不为任何线程拥有时才处于信号态,否则将处于非信号态。若要线程释放其所有权, 则线程在每次互斥对象处于非信号态时都调用函数 ReleaseMutex。当不再需要互斥对象 时可以使用函数 CloseHandle 来关闭互斥对象。当所有互斥对象的打开句柄都关闭时, 就删除互斥对象。
(2)OpenMutex
用于返回存在的已命名互斥对象的句柄。该函数允许多个进程打开同一个互斥对象 的句柄。该函数的调用一定要在函数 CreateMutex 创建互斥对象之后,当不需要句柄时 可以调用 CloseHandle 函数。
(3)ReleaseMutex
此函数用于释放互斥对象。若函数调用成功,互斥对象处于信号态。 (4)CreateSemaphore 此函数用于创建已命名或未命名的信号量对象,信号量用计数器实现同步。每次取信号量时(可利用函数WaitForSingleObject 来取),信号量计数器递减;每次ReleaseSemaphore 释放信号量值时,信号量计数器递增。计数永远不会小于 0 或大 于在 lSemMaxCount 参数中定义的值。
(5)OpenSemaphore
用于打开一个已经存在的命名的信号量对象。该信号量必须是 CreateSemaphore 创 建的。如果不再需要时,可以用函数 CloseHandle 关闭返回的句柄。
(6)WaitForSingleObject
此函数仅当在参数列表中指定的对象处于信号态或超过了超时间隔时,该函数才返 回。
(7)ReleaseSemaphore
用来递增信号量的计数。对于 CreateSemaphore 函数创建的对象使用,计数可以达 到设定的最大计数值。
2: windows 内核内存分为 paged memory 和 nonpaged memory,请问有什么区别? 答案:paged memory:是指可以分页的内存,可以交换到硬盘文件上。Nonpaged memory:不可分页,也就是不能交换到硬盘文件上。有些内存,比如驱动程序,内核代码是不允许交换出去的,应该常驻内存,就使用 nonpaged memory。
3:请问什么情况下,cache 中只放指令(数据直接从存储器存取)比 cache 中放数据和指令的效率高?
答案:计算密集型 cache 中只放指令(数据直接从存储器存取)比 cache 中放数据 和指令的效率高,可以充分利用指令的局部原理。
4:RISC 和 CISC 等其他指令集相比有哪些优点,请至少举出 5 个。
答案:(1)寻址方式少且简单,一般为 2—3 种,最多不超过 4 种,绝不出现存储器间接寻址方式。
(2)指令集中的指令数目一般少于 100 种,指令格式一般少于 4 种。
(3)指令功能简单,控制器多采用硬布线方式,以期更快的执行速度。
(4)平均而言,所有指令的执行时间为一个处理时钟周期。
(5)指令格式中用于指派整数寄存器的个数不少于 32 个,用于指派浮点数寄存器的个数不少于 16 个。
(6)强调通用寄存器资源的优化使用。
(7)支持指令流水并强调指令流水的优化使用。
5:选择题:
如果两个节点 x,y,preorder 遍历,x 在 y 之前,postorder 遍历,x 在y 之后,请问 x,y 的关系为:
A.x 是 y 的左兄弟。
B x 是 y 的右兄弟。
C.x 是 y 的祖先。 D x 是 y 的后裔。 答案:C。
6:请问下面程序如果运行会出现什么结果?如果有错误请指出并改正。
include <stdlib.h> include <string.h> class mystring{ public: mystring(){ m_str=NULL;
}
mystring(mystring& str){ if(m_str!=NULL){
delete []m_str;
}
m_str=new char[strlen(str.m_str)]; strcpy(m_str,str.m_str);
}
mystring & operator=(const char *str){ if(m_str!=NULL){
delete []m_str;
}
m_str=new char[strlen(str)+1]; strcpy(m_str,str);
}
~mystring(){ if(m_str!=NULL){ delete m_str;
}
}
private:
char *m_str;
};
int main(){ mystring str1; str1="hello world"; mystring str2; str2=str1;
mystring str3=str2;
return 0;
}
答案:程序运行会出现内存释放错误,错误共有四处,分别在下面改正的代码中标出。
include <stdlib.h> include <string.h>
class mystring{ public: mystring(){ m_str=NULL; } mystring(mystring& str){
/*if(m_str!=NULL){ delete []m_str; }*/
//错误 1,因为 m_str 没有被初始化,所以此处可能为 NULL,也可能不为 NULL, 如 果 不 为 null , 则 会 出 错 , 因 为 m_str 是 一 个 随 机 的 值 。 m_str=new
char[strlen(str.m_str)+1];
//错误 2:长度应该+1 strcpy(m_str,str.m_str); }
mystring & operator=(mystring& str){
//错误 3 : 缺少赋值重载函数 if(m_str!=NULL){ delete []m_str; } m_str=new
har[strlen(str.m_str)+1];
//错误 2:长度应该+1 strcpy(m_str,str.m_str); } mystring & operator=(const char
*str){ if(m_str!=NULL){ delete []m_str; } m_str=new char[strlen(str)+1]; strcpy(m_str,str); }
~mystring(){ if(m_str!=NULL){ delete []m_str;
//错误 4:析构函数中,应该析构数组 } } private: char *m_str; }; int main(){ mystring str1;
str1="hello world"; mystring str2; str2=str1; mystring str3=str2; return 0; }。
7:100 个乒乓球取胜之道,A,B两个人轮流拿,A先拿,一次只能拿[1,5]个, 获胜者为拿到最后一个球的人。请问 A 第一次该拿几个?以后又该怎么拿,才能够确 保获胜?
答案:A 先拿 4 个,然后 B 拿,设 B 拿 x 个,则每次 A 拿 6-x 个即可。
8:有编号 1-50 的人,依次排列,然后单号出列,然后剩下的人重新编号,单号 出列,依次类推,最后剩下一个人,请问这个人原来编号是多少号?如果是每一次双号 出列,请问这个人原来编号是多少?
答案:单号出列:32 号,双号出列:1 号。