C/C++2019秋招面试题集合02

C/C++2019秋招面试题集合02

8.24 【深信服】优招 - C++ 研发(物联网方向)

1 C++ 内存分区,未初始化的全局变量放在哪?如果编译了在二进制文件里会有他的位置吗?
答:C++虚拟内存包括栈区,MMP文件映射区,堆区,全局区,代码区(Text).其中全局区包括未初始化的bss区(此区默认值为0)和初始化的data区;代码区Text分为二进制文本区和只读存储区;文本区存放二进制代码,只读存储区存放字符串常量。 所以应该不会存在。

2 野指针是什么?有什么工具可以检测吗?
答:野指针:野指针就是指向一个未申请访问权限的指针,或者指向一个已经被删除的内存区域。一般在Linux会造成非法访问内存,报出段错误。所以我们申请的空间记得删除并且置为NULL。 具体的检测工具可以自己网上查一下。

3 进程间通信方式,知道互斥锁和自旋锁吗?
答:现今进程间常用通信方式有:1 管道 2 信号 3 mmp映射区 4本地套接字。 其中管道分为无名管道(pipe())和有名管道(makefifo()),无名管道用于有血缘关系的父子间,兄弟间通信;有名则不需要血缘关系。信号开销最小,但最复杂,不建议用。mmap有无血缘都可以用,但其API函数使用要很清楚,否则容易出错。 套接字通信是最常用的且最稳定的。
互斥锁:也叫互斥量,实际上是一个结构体,当多个对象或线程对同一个资源进行访问时,都需要上锁,访问完毕后解锁,防止数据出现混乱。下面以互斥锁配合条件变量为例子,写出最典型的生产者消费者案例(Linux实现):
先说一下消费者生产者要做的事情。

消费者:
1 访问数据之前先加锁
2 判断数据是否存在,存在则消费,不存在则阻塞在条件变量,并且解锁等待生产者生产。
3 访问消费数据
4 解锁
生产者:
1 先生产数据
2 访问数据之前先加锁
3 添加数据
4 解锁
5 通知消费者,使条件变量解除阻塞
6 最后循环生产数据

#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//数据节点
struct msg { 
    struct msg *next;
    int num;
};
struct msg *head;

pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; //这里使用静态方法给条件变量与锁直接赋值  没有调用函数动态赋值
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

//消费者的回调函数
void *consumer(void *p)  
{
    struct msg *mp;      //对数据操作的临时变量
    for (;;) {
        pthread_mutex_lock(&lock);       //1 上锁
        while (head == NULL) {           //2 先判断是否存在数据,头指针为空,说明没有数据节点  条件阻塞并解锁(wait函数的功能)  
        //可以为if吗 不可以否则多个消费来临时阻塞在条件变量,当生产者生产一个数据而被唤醒全部,数据不够分导致访问错误
            pthread_cond_wait(&has_product, &lock);
        }
        mp = head;      
        head = mp->next;    			//3 访问消费掉一个产品
        pthread_mutex_unlock(&lock);    //4 解锁

        printf("-Consume ---%d\n", mp->num);
        free(mp);                       //打印并释放被消费掉的数据
        sleep(rand() % 5);
    }
}
void *producer(void *p)
{
    struct msg *mp;
    while (1) {
        mp = malloc(sizeof(struct msg));
        mp->num = rand() % 1000 + 1;        //1 生产一个数据
        printf("-Produce ---%d\n", mp->num);

        pthread_mutex_lock(&lock);         //2 访问数据之前加锁
        mp->next = head; 
        head = mp;                         //3 访问添加数据
        pthread_mutex_unlock(&lock);       //4 解锁

        pthread_cond_signal(&has_product);  //5 将等待在该条件变量上的至少一个线程唤醒
        sleep(rand() % 5);            
    }  //最后加个循环生产
}
int main(int argc, char *argv[])
{
    pthread_t pid, cid;
    srand(time(NULL));

    pthread_create(&pid, NULL, producer, NULL); //创建两个线程
    pthread_create(&cid, NULL, consumer, NULL);

    pthread_join(pid, NULL);   //等待线程结束
    pthread_join(cid, NULL);
    return 0;
}	

自旋锁有兴趣的自己搜查学习。

4 一个结构体,能够用 memcpy 判断两个结构体存的东西是不一样的吗?
答:

5 知道哈希表吗?怎么解决冲突,如果只有 32 个槽,怎么存放几千个数据?
答:

6 路由器和二层交换机的区别?
答:路由器是工作在网络层,根据IP地址寻址,软件实现速度慢;而交换机工作在数据链路层,根据MAC地址寻址,硬件实现速度快。

7 用过 shell 脚本吗?
答:用过,推荐看鸟哥的私房菜这本书。用过shell脚本的例子:写makefile编译程序;给日志文件增加“i”选项保护文件;截取ifconfig的某个内容,循环创建多个用户等等。(个人而言)

8 知道二叉树有哪些遍历的方式吗?
答:递归的前中后序遍历和非递归遍历。
这里顺道给出非递归的遍历代码:(这里看不懂可以不看)
非递归思想:二叉树传过来后,先将根节点放进栈中,然后取出栈来判断他的节点标志位是否为true,是就打印显示;不是就重新入栈,标志位改为true,并且将其左右子节点也放进栈中,由于栈为先进后出,所以按前序就应放右节点后到左节点再到根节点。

//二叉树的非递归遍历
int UnRecursion(BinaryNode *root){

	//创建栈
	LinkStack *stack=Init_LinkStack();   //该栈为我自己写的企业链表模式

	//先创建一个二叉树节点放进栈中  MyStackNode为创建栈节点函数
	Push_LinkStack(stack,(LinkSNode*)MyStackNode(root,false));   //必须是放带有二叉树节点的节点进去


	//循环放与弹出
	while(Size_LinkStack(stack)>0){

		//先取出栈中元素
		BinTreeStackNode *pcur=(BinTreeStackNode*)Top_LinkStack(stack);
		Pop_LinkStack(stack);    //有这个就不用删除malloc的节点,里面做了

		//若二叉树节点为空则不作处理
		if(pcur->root==NULL){
			continue;
		}
		//若为真则显示
		if(pcur->flag==true){
			printf("%c",pcur->root->ch);
		}else{
			//否则就将它左右子树和本身放进栈中,并且本身变为true
			//前序,但因为栈是先进后出所以倒转
			Push_LinkStack(stack,(LinkSNode*)MyStackNode(pcur->root->rchild,false));
			Push_LinkStack(stack,(LinkSNode*)MyStackNode(pcur->root->lchild,false));
			pcur->flag=true;
			Push_LinkStack(stack,(LinkSNode*)pcur); 
			//这四句改一下就是中序,后序
		}


	}

	printf("\n");

	return 0;
}

9 后序遍历的实现?
答:递归对比非递归比较简单。`
int BinaryRecursion(BinaryNode *root){

//递归的条件
//任何一个节点作为根节点时,为空就返回,证明到尽头了
if(root==NULL){
	return -1;
}
//后序递归遍历-- DECBHGFA
//先遍历左子树
BinaryRecursion(root->lchild);

//再右子树
BinaryRecursion(root->rchild);

//最后输出相应的左子树节点
printf("%c",root->ch);

return 0;

}
`
10 假设 4 个人过河,每个人的过桥时间为 1,2,5,8。只有一个手电筒,一次最多过两个,怎么过桥速度最快?
答:1和2先过去,1回来,3分钟;然后5和8过去,2回来,10分钟;最后1和2过去,2分钟。3+10+2=15分钟。

11 如果是 n 个人怎么计算他的过河时间?
答:

12 arp 协议的功能干嘛用的?
答:以太网协议:根据MAC地址,完成数据包传输。ARP协议:根据IP地址寻址MAC地址。所以ARP协议是在数据链路层的以太网协议工作的。当数据封装好在以太网协议发包时,需要MAC地址,由于只知道数据包的IP地址,所以这时就需要ARP协议来将IP转为MAC,ARP首先发送ARP请求,被某个路由器接收后,路由器有一个路由表,记录着与他相连的多个路由器的IP地址,这样每一个路由器都能接收到这个请求,路由器IP地址与请求的IP地址一样就会相应,回发一个带有自己的MAC地址,这样就找到了MAC。IP不同的路由器会将该包扔掉。

13 快速排序的思想?
答: 思想:快速填坑。
1 挖坑填坑 第一个元素设为基准数被挖出来 然后从右到左找小于基准数的数 找到就填坑。
2 然后填坑的那个数相当于被挖了 这时从左往右找大于基准数的 找到就填坑 一直循环下去。
3 直到i<j就退出 然后将基准数填进i或者j的位置上 。
4 以上只是进行一次基准数填进 最后要递归将左右部分调用原函数排序。
5 所有的判断条件都要i<j。

//快速排序 --挖坑填坑
int QuickSort(int arr[],int start,int end){

	int i=start;
	int j=end;
	int tmp=arr[start];

	if(i<j){

		while(i<j){

			//先从右往左 找小于基数的数
			while(i<j && arr[j]>tmp){
				j--;
			}
			//填坑
			if(i<j){
				arr[i]=arr[j];
				i++;     //填完记得将i加1
			}

			//再从左往右 找大于基数的数
			while(i<j && arr[i]<tmp ){
				i++;
			}
			//填坑
			if(i<j){
				arr[j]=arr[i];
				j--;
			}
		}

		//最后将tmp填进i或j的下标中-----这样就进行了一次基数填进
		arr[j]=tmp;


		//然后循环填基数

		//把左半部分进行快速排序 确定的基数就不必再排序
		QuickSort(arr,start,i-1);
		//把右半部分进行快速排序
		QuickSort(arr,i+1,end);
	}
	
	return 0;
}

注:以上均为个人的答案,没写的欢迎大家补充。
题目来源LeetCode的ID为陈乐乐用户。答案自己查阅资料。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个是很经典的问 实验目: 生产者与消费者(综合性实验) 实验环境: C语言编译器 实验内容: ① 由用户指定要产生的进程及其类别,存入进入就绪队列。    ② 调度程序从就绪队列中提取一个就绪进程运行。如果申请的资源被阻塞则进入相应的等待队列,调度程序调度就绪队列中的下一个进程。进程运行结束时,会检查对应的等待队列,激活队列中的进程进入就绪队列。运行结束的进程进入over链表。重复这一过程直至就绪队列为空。    ③ 程序询问是否要继续?如果要转直①开始执行,否则退出程序。 实验目的: 通过实验模拟生产者与消费者之间的关系,了解并掌握他们之间的关系及其原理。由此增加对进程同步的问的了解。 实验要求: 每个进程有一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程类型标号、进程系统号、进程状态、进程产品(字符)、进程链指针等等。 系统开辟了一个缓冲区,大小由buffersize指定。 程序中有三个链队列,一个链表。一个就绪队列(ready),两个等待队列:生产者等待队列(producer);消费者队列(consumer)。一个链表(over),用于收集已经运行结束的进程 本程序通过函数模拟信号量的操作。 参考书目: 1)徐甲同等编,计算机操作系统教程,西安电子科技大学出版社 2)Andrew S. Tanenbaum著,陈向群,马红兵译. 现代操作系统(第2版). 机械工业出版社 3)Abranham Silberschatz, Peter Baer Galvin, Greg Gagne著. 郑扣根译. 操作系统概念(第2版). 高等教育出版社 4)张尧学编著. 计算机操作系统教程(第2版)习解答与实验指导. 清华大学出版社 实验报告要求: (1) 每位同学交一份电子版本的实验报告,上传到202.204.125.21服务器中。 (2) 文件名格式为班级、学号加上个人姓名,例如: 电子04-1-040824101**.doc   表示电子04-1班学号为040824101号的**同学的实验报告。 (3) 实验报告内容的开始处要列出实验的目的,实验环境、实验内容等的说明,报告中要附上程序代码,并对实验过程进行说明。 基本数据结构: PCB* readyhead=NULL, * readytail=NULL; // 就绪队列 PCB* consumerhead=NULL, * consumertail=NULL; // 消费者队列 PCB* producerhead=NULL, * producertail=NULL; // 生产者队列 over=(PCB*)malloc(sizeof(PCB)); // over链表 int productnum=0; //产品数量 int full=0, empty=buffersize; // semaphore char buffer[buffersize]; // 缓冲区 int bufferpoint=0; // 缓冲区指针 struct pcb { /* 定义进程控制块PCB */ int flag; // flag=1 denote producer; flag=2 denote consumer; int numlabel; char product; char state; struct pcb * processlink; …… }; processproc( )--- 给PCB分配内存。产生相应的的进程:输入1为生产者进程;输入2为消费者进程,并把这些进程放入就绪队列中。 waitempty( )--- 如果缓冲区满,该进程进入生产者等待队列;linkqueue(exe,&producertail); // 把就绪队列里的进程放入生产者队列的尾部 void signalempty() bool waitfull() void signalfull() void producerrun() void comsuerrun() void main() { processproc(); element=hasElement(readyhead); while(element){ exe=getq(readyhead,&readytail); printf("进程%d申请运行,它是一个",exe->numlabel); exe->flag==1? printf("生产者\n"):printf("消费者\n"); if(exe->flag==1) producerrun(); else comsuerrun(); element=hasElement(readyhead); } printf("就绪队列没有进程\n"); if(hasElement(consumerhead)) { printf("消费者等待队列中有进程:\n"); display(consumerhead); } else { printf("消费者等待队列中没有进程\n"); } if(hasElement(producerhead)) { printf("生产者等待队列中有进程:\n"); display(producerhead); } else { printf("生产者等待队列中没有进程\n"); } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值