一、思维导图
二、模拟面试
结构体中一个char,一个int 结构体占字节长度是多少?描述一下结构体字节对齐规则?怎样改成两字节对其? | 答: 8字节; 结构体中每个变量自己先要符合字节对齐原则,即该变量的起始位置需要与自己字节大小的整数倍对齐;结构体自身要满足字节对齐原则,结构体中字节最大的变量与8比较,其中的较小的字节数就是结构体整体的对齐原则,即结构体最终大小的最后一个字节要与该字节数对齐; 使用#pragma pack(push, 2)和#pragma pack(pop)来设置结构体的两字节对齐 如: #pragma pack(push, 2) struct MyStruct { 。。。 。。。 }; #pragma pack(pop) |
描述链表、栈、队列各自特点 | 答: 链表是链式存储的操作不受先限线性表,是具有存储结构和逻辑结构的一种工具,可以将堆区零散的内存利用起来构成逻辑上相邻的数据元素; 而栈和队列都是操作受限的线性表,都只是一种逻辑结构,可以与顺序存储和链式存储相结合构成顺序栈、顺序队列、链式栈、链式队列这样的工具来存储和处理数据; 栈的逻辑是数据元素后进先出(LIFO),队列的逻辑是数据元素先进先出(FIFO),栈只能在容器的一端对数据进行入栈和出栈操作,而队列是在容器的两端分别进行入队和出队操作。 |
标准IO和文件IO的区别 | 答: IO就是程序和外部设备进进行信息交互的过程,标准IO就是调用封装好的库函数来进行数据的输入输出,而文件IO是调用内核自带的函数来实现数据的输入输出; 标准IO有缓冲区,文件IO没有缓冲区,所以标准IO的效率要高于文件IO; 标准IO是对文件指针进行操作,而文件IO是对文件描述符进行操作; 标准IO主要用于程序和用户的交互,而文件IO主要用于程序和文件系统的交互。 |
声明变量和定义变量的区别 | 答: 声明变量不分配内存空间,定义变量分配内存空间; 声明变量一般用在头文件中告知源文件存在该变量, 定义变量一般在源文件中直接对该变量进行实质性的操作; 变量和函数可以被重复声明,但是定义只能有一次,不能被重复定义。 |
什么是被调函数,如何完成函数的被调 | 答: 被调函数就是在程序中被主函数或者其它函数使用的函数; 需要先定义函数,根据函数需要实现的功能决定有无返回值,有无参数,完善相应的函数功能,在主调函数里直接使用被调函数的名字加上括号传入被调函数对应类型的实参即刻调用被调函数,如果被调函数有返回值也可以使用对应返回类型的变量直接用赋值符号将被调函数的返回值接收。 |
gcc分布编译的流程 | 答: 预处理 gcc -E test.c -o test.i 编译 gcc -S test.i -o test.s 汇编 gcc -c test.s -o test.o 链接 gcc test.o -o test 生成 可执行文件test 预处理:展开头文件,替换宏定义,删除注释; 编译:检查语法问题,出现错误会报错,编译停止, 没有错误会生成汇编文件; 汇编:生成一个不可执行的二进制文件; 链接:链接库函数将不可执行二进制文件生成可执行 二进制文件。 |
结构体和共用体的区别 | 答: 结构体和共用体都是包含不同类型成员变量的数据结构; 结构体里不同类型成员的存储需要满足结构体存储的对齐原则,会存在内存间隙, 而共用体内不同类型的成员变量公用同一片内存空间,使用最大成员变量大小申请内存,通常是最大成员类型大小的整数倍,不需要进行对齐,不会造成内存浪费,但同一时间只能对一个成员变量进行有效操作; 结构体适用于同时对多个类型的成员变量进行访问和操作,而共用体只适用同时对一个类型的成员变量进行操作。 结构体成员各自独立,不存在大小端问题,而共用体成员使用同一片空间,可以判断大小端问题 |
形参和实参的区别 | 答: 形参是函数定义时括号内部声明的参数,是函数期待接受的参数,是一种虚拟的占位符,实参是函数调用时传入函数的具体的数值或者变量; 形参只有在函数被调用时才会分配内存,函数结束则释放,所以形参的改变不会直接造成实参的改变除非使用地址传递,传递参数的地址,在函数中直接对地址对应的数据进行操作,实参需要在调用函数传参前被定义好再传入函数,实参的存在时间取决于它的定义; 形参在函数定义时可以根据需求使用任意数据类型,但是使用实参进行传参时必须要对应形参的数据类型使用明确数据类型的实参进行传参。 |
三、练习题
1、使用文件IO完成,将源文件中的所有内容进行加密(大写转小写、小写转大写)后写入目标文件中,源文件内容不变。
#include<myhead.h>//定义加密函数
int encrypt(char *str,int n)
{//判断逻辑if(n <= 0){printf("encrypt error");return -1;}//循环对字符数组进行加密for(int i=0;i<n;i++){//大写字母转小写字母if(*(str+i) > 'A' && *(str+i) <'Z'){*(str+i) += 32;}//小写字母转大写字母if(*(str+i) > 'a' && *(str+i) <'z'){*(str+i) -= 32;}}
}/****************主函数****************/
int main(int argc, const char *argv[])
{//判断终端输入if(argc != 3){printf("input error\n");printf("usage:./a.out srcfile destfile\n");return -1;}//定义文件描述符int srcfd = -1;int destfd = -1;//源文件以只读的形式打开if((srcfd = open(argv[1],O_RDONLY)) == -1){perror("open src error");return -1;}//目标文件的打开方式为读写if((destfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,0664 )) == -1){perror("open dest error");return -1;}//定义容器转运源文件中的数据char buf[128];//将源文件中的数据搬运到buf中然后加密后转存进目标文件中while(1){//将源文件的数据循环部分加载到容器中,并返回读取的大小int res = read(srcfd,buf,sizeof(buf)); if(res == 0){break;}//调用加密函数将读取到容器中的数据加密后写入到目标文件中encrypt(buf,res);write(destfd,buf,res);}//关闭文件描述符close(srcfd);close(destfd);printf("encrypt success!\n");return 0;
}
2、查资料了解以下概念:
并发和并行的区别
- 定义
- 并发:并发是指在同一时间段内,多个任务交替执行,看似同时进行,但实际上是系统通过快速切换这些任务来实现的多任务处理效果。
- 并行:并行是指在同一时刻,多个任务真正地在不同处理器或核心上同时执行。
- 执行方式
- 并发:任务是线性执行的,系统通过将时间分割成极短的时间片,并轮换分配给各个任务,从而给用户造成同时进行的假象。
- 并行:任务被分割成子任务,由不同的处理器或核心独立执行这些子任务,实现真正的同步执行。
- 资源占用
- 并发:多个任务共享同一个处理器,通过快速上下文切换来执行,任务之间在宏观上是同时进行的。
- 并行:每个任务(或子任务)有独立的处理器或核心,因此可以完全独立地执行,不相互抢占资源。
- 适用场景
- 并发:适用于需要提高系统响应和吞吐量的场景,如多任务操作系统和网络服务器等。
- 并行:适用于加速计算密集型任务,如科学计算、图像处理、机器学习等需要大量计算的场景。
- 效率问题
- 并发:虽然提高了任务的处理能力,但由于任务切换带来的开销,可能会影响总体效率。
- 并行:能够真正提高计算效率,但依赖于硬件的支持,如多核处理器和分布式系统。
- 逻辑物理
- 并发:是逻辑上的同时发生,通过时间片机制实现多任务处理。
- 并行:是物理上的同时发生,多个处理器或核心真正同步执行任务。
- 用户体验
- 并发:用户感知不到任务切换的过程,看到的是多个任务同时进行。
- 并行:用户可以看到任务在真正意义上同时执行,性能提升显著。
- 编程实践
- 并发:需要开发者精心设计任务调度和资源管理,避免因任务切换导致的性能瓶颈。
- 并行:要求开发者利用多线程、多进程或分布式系统实现任务的真正并行处理,提高代码的执行效率。
什么是进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,也是操作系统结构的基础 。
进程和程序的区别
- 实体性质
- 程序:程序是静态的,它代表了一组保存在某个存储介质上的有序指令集合。
- 进程:进程是动态的,对应于程序的一次具体执行过程,包括创建、运行、暂停和终止等状态变化。
- 存储位置
- 程序:程序通常存储在硬盘等持久存储设备上,直到被执行前不会占用系统运行时资源。
- 进程:进程在运行时需要加载到内存中,由中央处理单元(CPU)直接执行其指令。
- 动态特性
- 程序:程序本身不具有动态性,它是永久存在的,只要不被删除就可以一直存储在介质中。
- 进程:进程具有高度的动态性,它在被创建后处于不断变化的状态中,并在完成特定任务后终止。
- 并发特性
- 程序:程序本身无法并发执行,它需要被操作系统调度和管理才能并发运行。
- 进程:进程具有并发性,多个进程可以在多任务操作系统中同时运行。
- 独立性
- 程序:程序不能独立运行或获得资源,它只是一组被动的指令集合。
- 进程:进程是独立运行和资源调度的基本单位,可以独立地获取和使用系统资源。
- 资源分配
- 程序:程序对资源的需求较小,只需要存储指令的空间。
- 进程:进程在其生命周期内需要各种资源,如CPU时间、内存地址空间和I/O设备。
- 控制块组成
- 程序:程序由代码和数据两部分组成,没有进程控制块(PCB)。
- 进程:进程不仅包含程序代码和数据,还有自己的PCB,用于管理进程的状态和资源。
- 执行关系
- 程序:一个程序可以对应多个进程,即同一个程序在不同数据集上运行可以构成多个不同的进程。
- 进程:每个进程都是独立的执行实例,可以顺序或并发地执行程序代码。
进程的状态有哪些
进程的状态主要有五种基本状态和两种衍生状态。
- 创建状态
- 定义:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,并完成资源分配。如果创建工作无法完成(如资源不足),则无法被调度运行,此时所处的状态称为创建状态。
- 特点:此状态下的进程正在初始化,尚未准备好运行。
- 就绪状态
- 定义:进程已经准备好运行,已分配到所需资源,只要分配到CPU就能够立即运行。
- 特点:进程等待被操作系统调度,随时准备执行。
- 执行状态
- 定义:进程处于就绪状态被调度后,进入执行状态。此时进程正在使用CPU执行其指令。
- 特点:进程正在占用CPU资源,实际执行其任务。
- 阻塞状态
- 定义:正在执行的进程由于某些事件(如I/O请求、申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用。
- 特点:进程暂时停止执行,等待某个条件或资源的满足。
- 终止状态
- 定义:进程结束或出现错误或被系统终止,进入终止状态。无法再执行。
- 特点:进程已完成执行或异常终止,等待系统清理。
- 挂起状态
- 定义:当系统需要减少内存占用或用户要求暂缓进程执行时,进程可以被挂起,状态保存到磁盘上。
- 特点:进程暂时不在内存中,状态保存在磁盘上,不占用系统资源。
- 激活状态
- 定义:当系统资源充足或用户需要恢复挂起的进程时,进程可以从挂起状态被激活,重新进入就绪或阻塞状态。
- 特点:用于恢复挂起的进程,使其重新参与调度。
系统中的多个进程的调度机制都有哪些
- 非剥夺调度方式
- 定义:当一个进程正在处理机上执行时,即使有更重要或紧迫的进程进入就绪队列,也仍让当前进程继续执行,直到它完成或发生阻塞时才将处理机分配给其他进程。
- 优点:实现简单,系统开销小,适用于大多数批处理系统。
- 缺点:不能用于分时系统和大多数实时系统,因为它不利于紧急任务的处理。
- 剥夺调度方式
- 定义:当一个进程正在处理机上执行时,若有优先级更高的进程需要使用处理机,则立即暂停当前进程,将处理机分配给优先级更高的进程。
- 优点:提高系统吞吐率和响应效率,适用于需要快速响应的实时系统。
- 缺点:增加系统开销,需要更复杂的调度策略和管理机制。
- 时间片轮转算法
- 定义:所有就绪进程排成一个队列,每个进程被分配固定的时间片,时间片用完进程移至队尾,等待下一次调度。如果时间片内完成则直接移出队列。
- 优点:公平性好,避免了长作业饥饿现象,提高了系统响应时间。
- 缺点:可能导致处理器频繁切换进程,增加上下文切换开销。
- 短作业优先算法
- 定义:选择预估运行时间最短的进程进行调度,分为非剥夺式和剥夺式两种。
- 优点:平均等待时间和周转时间较少,提高系统吞吐量。
- 缺点:可能导致长作业“饥饿”,作业的预估时间可能不准确。
- 优先级调度算法
- 定义:根据进程优先级进行调度,可以是静态(创建时确定且不变)或动态(随进程推进或等待时间变化)优先级。
- 优点:确保重要或紧迫的进程及时获得CPU时间。
- 缺点:低优先级进程可能饥饿,响应时间无法保证。
- 多级反馈队列算法
- 定义:设置多个就绪队列,每个队列赋予不同的优先级,新进程首先进入第一队列末尾,按FCFS原则等待调度,若长时间未获得CPU则升级至高优先级队列。
- 优点:综合多种算法的优点,兼顾公平性和系统性能。
- 缺点:实现较为复杂,需权衡不同参数以达到最优效果。