文章目录
C/C++笔试练习
选择部分
(1)fork()
下面的程序执行输出几个hello?
#include<stdio.h>
#include <unistd.h>
int main( )
{
fork( );
fork( );
fork( );
printf(“hello\n”);
return 0;
}
A.3
B. 4
C.6
D.8
答案:D
#include<stdio.h>
#include <unistd.h>
int main()
{
//假设fork创建子进程都会创建成功
//该fork执行之后,会创建出来一个子进程,加上本身的父进程,就会有两个进程
fork();
//进而在产生两个子进程,加上之前的两个,总共4个进程
//上面的两个进程都会执行该fork,
fork();
//上面的4个进程都会执行该fork,进而在产生4个子进程,加上之前的4个,总共8个进程
fork();
//所以,最终有8个进程,会执行下面的语句
printf(“hello\n”);
return 0;
}
(2)文件读写
有一个程序中有A,B,C三个线程同时对一个文件进行读写操作,其中的A,B是写进程只负责往里面写数据,C是读线程,同时把读取的数据从文件中删除,A线程单独写满文件
需要10个小时,B单独写程序需要6小时,C线程需要15小时才能读取完整个文件,不考虑三个线程之间的相互影响的情况下现在__小时才能写满文件
A.5
B. 6
C.5.5
D.4.5
E. 4.8
F. 5.3
答案:A
(1)不考虑三个线程之间的相互影响,突然回到了小学数学题:“两个水龙头注水,一个放水,问何时注满水池”的感觉。这样问题就简单了。
(2)总任务是单位1,A的写工作效率是1/10,B的写工作效率是1/6,C的读工作效率是1/15,那么总工作效率是1/10+1/6-1/15 结果是1/5。
(3)于是,写满的总时间是:用单位1除以总工作效率五分之一,就是5小时。
(3)系统内存
系统中内存不足程序所需大小,程序就无法执行。
A.错
B.对
答案:A
操作系统存在虚拟内存,能够把一部分优先级较低的程序保存到系统硬盘。
(4)存储保护
通常所说的"存储保护"的基本含义是()
A.防止存储器硬件受损
B.防止程序在内存丢失
C.防止程序间相互越界访问
D.防止程序被人偷看
答案:C
存储保护的含义就是防止 程序之简互相越界访问/篡改对方的数据,从而导致对方程序出现问题。
(5)进程调度
下列进程调度算法中,()可能会出现进程长期得不到调度的情况。
A.非强占式静态优先权法
B.强占式静态优先权法
C.时间片轮转调度算法
D.非强占式动态优先权法
答案:B
(1)强占式: 现行进程在运行过程中,如果有重要或紧迫的进程到达(其状态必须为就绪),则现运行进程将被迫放弃处理机,系统将处理机立刻分配给新到达的进程。
(2)静态优先权: 在创建进程时确定的,优先权在进程的整个运行期间保持不变。
(3)动态优先权: 在创建进程时所赋予的优先权,是可以随进程的推进或随其等待时间的增加而改变的,以便获得更好的调度性能,该优先权会随着等待的时间增长而增长。
非强占式静态优先权法-优先权不会变,假如有一个最低优先权的线程在等待,那么等优先级高的进程执行完就可以轮到该进程执行了。
强占式静态优先权法-强占式,说明高优先权的可以抢夺CPU的执行权。假如一个低优先权的执行当一个高优先权的在就绪状态,那么CPU就会执行高优先权的那个进程,低优先权的就会处于等待,假如一直有高优先权的进程在就绪,那么就会一直等待。
时间片轮转调度算法-执行的时间片完毕后,被执行的进程会放到等待队列的队尾,一次循环,既然是循环的话,那就有机会轮到。
非强占式动态优先权法-虽然是非强占式的,但是如果一个进程一直在等待,那么他的优先权就会动态增长,就可以得到CPU的执行权
(6)信号量
如果信号量的当前值为-4,则表示系统中在该信号量上有()个进程等待。
A.4
B. 3
C.5
D.0
答案:A
信号量=资源计数器+PCB等待队列。
(1)count值大于0,表示这个信号量是空闲的(或者理解为资源还可以被使用),进程(线程)可以使用这个信号量。
(2)count值等于0,表示信号量被其他进程使用,现在不可以用这个信号量,但PCB等待队列也没有进程在等待。
(3)count值小于0,count的绝对值表示有多少个进程(线程)在等待。
所以本题表示还有4个进程在等待获取信号量。
(7)互斥信号量
设两个进程共用一个临界资源的互斥信号量mutex=1,当mutex=-1时表示()
A.一个进程进入了临界区,另一个进程等待
B.没有一个进程进入临界区
C.两个进程都进入临界区
D.两个进程都在等待
答案:A
互斥信号量,初始值为1,取值范围为(-1,0,1)。
当信号量为1时,表示两个进程皆未进入需要互斥的临界区。
当信号量为0时,表示有一个进程进入临界区运行,另一个必须等待。
当信号量为-1时,表示有一个进程正在临界区运行,另一个进程因等待而阳塞在信号量队列中,需要当前已在临界区运行的进程退出时唤醒。
(8)用户级线程
若系统中只有用户级线程,则处理机调度单位是()
A.线程
B.进程
C.程序
D.作业
答案:B
如果系统只有用户态线程,则线程对操作统是不可见的,操作系统只能调度进程;如果系统中有内核态线程,则操作系统可以按线程进行调度。
D选项的作业:作业可以包括多个进程,完成某一个功能,和题意不符合
(9)cache缓冲
一个在线服务器通常需要读取存储着海量数据的数据库。为了提高服务器处理速度,通常需要加cache(缓存),以下场景中不适合使用cache的是()
A.数据库中每条数据被访问的概率近似相等,且独立
B.使用了多线程机制的服务
C.单条线程尺寸太小的数据
D.有着大量访问的服务
答案:A
缓存本质上是将在磁盘的数据提前缓存在内存当中,从而减少程序访问获取数据时的I0操作次数缓存一般会将高频访问的数据,提前读到内存当中缓存起来。
A选项:访问的概率相等,且独立,就失去了缓存的意义,因为无法区分到底缓存什么数据,能够提高程序的运行效率。除非全部缓存,对表的概念就是内存数据库。
B.多线程程序为了减少线程I0的时间,可以把线程使用的数据提前缓存起来, 提高程序的运行效率。
C选项:尺寸较小的数据可以放到缓存当中,因为比较小,不会吃掉缓存的空间。同时缓存之后,也能提高程序的运行效率。
D选项:大量的访问的服务想要访问的数据可以缓存下来,减少0次数,提高程序的运行效率。
(10)操作系统功能
计算机操作系统的主要功能是()
A.管理计算机系统的软硬件资源,以充分发挥计算机资源的效率,并为其它软件提供良好的运行环境
B.把高级程序设计语言和汇编语言编写的程序翻译到计算机硬件可以直接执行的目标程序,为用户提供良好的软件开发环境
C.对各类计算机文件进行有效的管理,并提交计算机硬件高效处理
D.为用户提供方便地操作和使用计算机
答案:A
计算机的主要功能:管理计算机系统的软硬件资源;其中管理=描述(定义结构体)+组织(将结构体串联起来)。
编程题 day28
反转部分单向链表
解题思路:法一,找到需要反转部分链表的起始位置,断链反转之后,再进行恢复链表输出。
list_node* ReverseList(list_node* head)
{
list_node* prev = nullptr, *cur = head;
while (cur)
{
list_node* next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
return prev;
}
list_node* reverse_list(list_node* head, int L, int R)
{
list_node* newHead = new list_node;
newHead->next = head;
list_node* prevNode = newHead, *RHead = nullptr, *RTail = nullptr,
*TailNext = nullptr;
for (int i = 0; i < L - 1; i++)
{
prevNode = prevNode->next;
}
RHead = prevNode->next;
RTail = RHead;
for (int i = 0; i < R - L; i++)
{
RTail = RTail->next;
}
TailNext = RTail->next;
RTail->next = nullptr;
list_node* RNewHead = ReverseList(RHead);
prevNode->next = RNewHead;
RHead->next = TailNext;
return newHead->next;
}
法二:法一的弊端:假设需要反转的链表部分,占比比较大,则需要两次遍历链表来实现。(1,遍历确定反转链表的起始位置 2,遍历链表进行反转)。
那是不是可以考虑一次遍历链表就解决该问题呢?
这里的思路是:采用头插的方式一次遍历解决问题。
list_node* reverse_list(list_node* head, int L, int R)
{
list_node* pHead = new list_node;
pHead->next = head;
list_node* prevNode = pHead;
for (int i = 0; i < L - 1; i++)
{
prevNode = prevNode->next;
}
list_node* cur = prevNode->next;
for (int i = 0; i < R - L; i++)
{
list_node* nextNode = cur->next;
cur->next = nextNode->next;
nextNode->next = prevNode->next;
prevNode->next = nextNode;
}
list_node* list = pHead->next;
free(pHead);
return list;
}
猴子分桃
解题思路:因为每次分5堆都会多出来1个,所以我们借给猴子们4个,以致每次都可以刚好分成5堆 并且,每次给老猴子的桃子都不在我们借出的那4个中,这样最后减掉4就可以得到结果。 假设最初由x个桃子,我们借给猴子4个,则此时有x+4个, 第一个猴子得到(x+4)/5,剩余(x+4)(4/5)个 第二个猴子分完后剩余(x+4) (4/5) ^ 2个 第三个猴子分完后剩余(x+4) (4/5) ^ 3个 依次类推,第n个猴子分完后剩余(x+4)(4/5)^ n 要满足最后剩余的为整数,并且x最小,则当 x+4=5 ^ n时,满足要求;此时,x=5 ^ n - 4; 老猴子得到的数量为:x+4)*(4/5) ^ n + n - 4= 4 ^ n + n - 4 最后的 +n是因为每个小猴子都会多出一个给老猴子,-4是还了借的4个。
#include <iostream>
#include <string>
#include <math.h>
int main()
{
int n;
while (std::cin >> n)
{
if (n == 0) break;
long total = pow(5, n) - 4;
long left = pow(4, n) + n - 4;
std::cout << total << " " << left << std::endl;
}
return 0;
}