C++笔试笔记2
百富计算机的笔试
-
const限定符:首先作用于左边,如果左边没东西,就作用于右边。
const int: 左边没有内容,所以const作用于右边,就是“整型常量”。等同于int const;
int * const:左边是指针,所以const作用于指针上,指针本身(地址)不能被修改,是常量指针; //顶层const
const int*:左边没东西,所以const作用于int上,指针指向的内容不可改变,如const int *a,则(*a)不可改变。 //底层const可以将一个非const对象的地址赋给一个指向const对象的指针。
可以将一个非const对象的地址赋给一个指向非const对象的指针。
一个const对象不可以被多次赋值。 -
在C程序中,%是只能用于整数运算的运算符。
-
定义int sum[10],*m=sum,*n = &sum[4];,那么n-m的值是?
此时n-m并不是16,而是4。
在C的标准中规定,加法与减法运算对于地址的操作和对于值的操作是不同的。 地址之差是数组下标之差,相对于地址值的差除以一个数组元素的size。 -
strcat函数用于连接两个字符串。
-
进程和线程的区别:
a.进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。
b.进程——资源分配的最小单位,线程——程序执行的最小单位。
c.在linux 中,进程比线程安全的原因是,多进程下,每个进程都有自己独立的地址空间,进程间的数据空间也相互独立,彼此通信以专门的通信方式进行;而多线程下,同一个进程内的线程共享进程的地址空间,一个线程的数据可以提供给其他线程使用。而且当一个线程处于临界区,并且没有释放临界区就终止时,当其他线程也要访问临界区时,就会引起死锁,从而引起不安全的现象。 -
孤儿进程和僵尸进程:
孤儿进程:
init进程就是pid为1的进程,就算Linux内核中的第一个进程。僵尸进程:
-
TCP和UDP:
UDP协议:面向无连接(不需要三次握手和四次挥手)、尽最大努力交付、面向报文(每次收发都是一整个报文段)、没有拥塞控制不可靠(只管发不管过程和结果)、支持一对一、一对多、多对一和多对多的通信方式、首部开销很小(8字节)。优点是快,没有TCP各种机制,少了很多首部信息和重复确认的过程,节省了大量的网络资源。缺点是不可靠不稳定,只管数据的发送不管过程和结果,网络不好的时候很容易造成数据丢失。又因为网络不好的时候不会影响到主机数据报的发送速率,这对很多实时的应用程序很重要,因为像语音通话、视频会议等要求源主机要以恒定的速率发送数据报,允许网络不好的时候丢失一些数据,但不允许太大的延迟,UDP很适合这种要求。
TCP协议:是TCP/IP体系中非常复杂的一个协议,面向连接(需要三次握手四次挥手)、单播(只能端对端的连接)、可靠交付(有大量的机制保护TCP连接数据的可靠性)、全双工通讯(允许双方同时发送信息,也是四次挥手的原由)、面向字节流(不保留数据报边界的情况下以字节流的方式进行传输,这也是长连接的由来。)、头部开销大(最少20字节)。优点是可靠、稳定,有确认、窗口、重传、拥塞控制机制,在数据传完之后,还会断开连接用来节约系统资源。缺点是慢,效率低,占用系统资源高,在传递数据之前要先建立连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接。在要求数据准确、对速度没有硬性要求的场景有很好的表现,比如在FTP(文件传输)、HTTP/HTTPS(超文本传输),TCP很适合这种要求。 -
gcc编译选项中,用于指定动态库搜索目录的是-L。
-
把编译分为编译阶段和链接阶段的话,如果报错:undefined reference to ‘xxxx’,一般是链接阶段失败,找不到该函数或者变量的定义。
-
创建线程和进程的函数分别是pthread_create()和fork().
-
进程间通信(IPC)的方式:
联想嵌入式软件笔试(20道单选,10道多选)
-
C语言不是中级语言,是高级语言;
-
ARM伪指令有四个,分别是LDR、ADR、ADRL和NOP。它们的具体描述可以看这个
-
ARM的协处理器中CP7~CP4用作虚拟化和调试;
-
NAND Flash控制器的特征?
-
Linux系统C标准头文件存储路径为/usr/include;
-
linux系统进程类型有 :交互进程 ;批处理进程 ;监控进程(守护进程);
交互进程:由一个shell启动的进程。交互进程既可以在前台运行,也可以在后台运行。
批处理进程:这种进程和终端没有联系,是一个进程序列。
监控进程(也称守护进程):Linux系统启动时启动的进程,并在后台运行。可以用来长时间执行需要在后台运行、不需要与用户交互的任务。在Linux中,批处理进程(Batch Process)通常是指一批需要按照特定的顺序和规则自动执行的作业。这些作业可以由shell脚本或其他编程语言编写而成,然后交给批处理系统进行调度和管理。
具体来说,Linux中的批处理进程可以使用如下方式来实现:- 使用编写好的shell脚本,包含需要执行的命令和参数,使用cron等工具来进行定时调度。cron可以在指定的时间点自动启动shell脚本,让其自动执行。
- 使用at工具来进行一次性的调度。用户可以在at命令行中输入需要执行的命令和时间,at会将这些作业加入到队列中,指定时间到达后自动执行。
总之,在Linux中,批处理进程通过将一系列任务集合成作业,然后利用cron或at等调度器进行自动化调度和管理,提高了作业的执行效率和可靠性。
Linux守护进程可以用来长时间执行需要在后台运行、不需要与用户交互的任务,例如:
- 作为服务端程序运行。很多服务端程序需要一直在后台运行,等待客户端的连接请求,这时使用守护进程可以保证服务端程序的稳定性和可靠性。
- 批量处理任务。使用守护进程可以让批量任务在后台自动运行,提高了任务的效率和可靠性。
- 日志处理。可以使用守护进程来监控某些特定的日志文件,当日志文件发生变化时,立即进行处理,如发送邮件或短信提醒等。
总之,Linux守护进程可以让用户在后台中安全地运行某些耗时的任务,同时也可以有效地减轻系统的压力,提高系统的可用性和可靠性。
-
C语言中,有一些预处理定义的符号串,它们的值或者是字符串常量,或者是十进制数字常量,它们通常在调试程序时用于输出源程序的各项信息 。
-
提高CPU运算能力的方案有:
- 使用高性能的CPU;
- 增加CPU的核心数量。较多CPU核心意味着CPU可以同时处理更多任务,提高计算速度;
- 增加CPU的时钟频率。时钟频率是CPU的基本工作速度,因此增加频率可以提高计算速度;
- 加装更多内存。RAM可作为CPU的缓存提高计算效率,大量运用内存也可以避免频繁的硬盘读写操作。将内存增加到计算机支持的最大内存容量可以大幅提高计算机的运行速度;
- 超线程计算。它允许在一个物理内核上运行多个线程。当超线程技术处于激活状态时,CPU会在每个物理内核上公开两个执行上下文,从而使一个物理内核能够同时处理两个线程,有效地提高了计算机的并行处理能力和整体性能。因此,在需要高并发处理的应用场景中,启用超线程技术可以显著提升CPU的运算能力。
-
指令和数据都需要保存在存储器中,根据保存方式的不同,计算机可分为哪两种不同的架构?
- Von Neumann架构: 该架构是以计算机科学家冯·诺伊曼 (John von Neumann)命名的。在这种架构中,指令和数据都保存在同一个存储器中,并且按照相同的格式来表示。CPU 从存储器中读取指令和数据,并使用指令对数据进行操作。这种架构简单,易于实现,但存在指令和数据竞争的问题。
- Harvard架构: 该架构以哈佛大学命名,该架构中,指令和数据分别保存在不同的存储器中,并使用不同的总线传输。CPU可以同时从指令和数据存储器中读取信息,因此它可以更快地执行指令。但这种架构需要更复杂的硬件和软件实现。
-
MMU(Memory Management Unit,内存管理单元)在进行内存映射时是将逻辑地址转换为物理地址。
-
嵌入式系统的开发工具有什么
-
Keil μVision IDE:Keil μVision 是一款针对 ARM 及其他处理器架构的嵌入式系统开发工具,集成了编辑器、编译器、调试器等多种功能,为嵌入式系统开发提供了全面的支持。
-
IAR Embedded Workbench:IAR Embedded Workbench 是一款流行的嵌入式软件开发工具,支持多种处理器架构,包括 ARM、MIPS、Renesas 等。它集成了编辑器、编译器、调试器等多种功能,广泛用于嵌入式系统的开发。
-
Eclipse IDE:Eclipse 是一款流行的开源 IDE,支持多种编程语言和处理器架构,也可以用于嵌入式系统开发。通过安装适当的插件,可以在 Eclipse 中编写和调试嵌入式 C/C++ 代码。
-
Visual Studio Code:Visual Studio Code 是 Microsoft 开发的免费开源代码编辑器,支持多种编程语言和处理器架构,也可以用于嵌入式系统开发。通过安装适当的插件,可以在 Visual Studio Code 中编写和调试嵌入式 C/C++ 代码。
-
-
嵌入式软件级别的优化主要包括以下几种:
-
算法优化:选择合适的算法,可以在保证正确性的前提下,降低代码复杂度和执行时间。
-
数据结构优化:合理选择数据结构可以提高程序的执行效率。例如,使用数组代替链表,使用查找表代替搜索等。
-
编译器优化:编译器可以对源代码进行多种优化,例如循环展开、代码重排、变量复用等。开启编译器的优化选项可以有效提高程序的执行效率。
-
内存优化:嵌入式设备的内存资源通常很有限,因此需要专门进行内存优化。例如,使用静态分配代替动态分配,避免内存碎片等。
-
代码优化:使用一些小技巧可以提高代码的执行效率,例如使用位运算代替算术运算、减少函数调用、使用宏定义代替函数调用等。
-
-
ARM 处理器支持以下七种异常(Exception):
-
重置(Reset):电源复位或外部复位信号触发的异常,用于初始化处理器的状态。
-
非屏蔽中断(Undefined Instruction):当处理器执行未定义的指令时会触发此异常,一般用于实现模拟器等调试工具。
-
软件中断(Software Interrupt):处理器执行软件中断指令(SVC)时,将触发此异常。用于在用户态下请求系统服务。
-
延迟中断(Prefetch Abort):由于指令预取器(Prefetch Unit)无法从存储器中获取正确的指令时,将触发此异常。
-
数据中断(Data Abort):处理器尝试读取或写入存储器时,产生地址错误、访问权限不匹配等情况时触发此异常。
-
IRQ 中断(Interrupt Request):外部设备向处理器发送中断请求信号时,处理器将触发此异常。
-
FIQ 快速中断(Fast Interrupt Request):针对一些需要快速响应的外设,处理器提供了 FIQ 异常,可以更快地响应这些外设的中断请求。
需要注意的是,除重置异常以外,其余异常均可被屏蔽,其中 IRQ 和 FIQ 异常可以通过处理器中的相应控制寄存器来开启或关闭。
-
-
单片机应用系统的结构分为以下几个层次:
-
应用层:实现具体应用功能的层次。该层次包括主程序和各种功能模块,例如输入输出模块、控制模块等。
-
中间层:建立在硬件层之上,提供软件抽象和框架的层次。该层次包括操作系统、库函数、驱动程序等。
-
硬件层:单片机的物理基础,包括CPU、存储器、输入输出接口等。
这些层次在实际应用中相互联系、相互作用,形成了一个完整的单片机应用系统。
-
-
一个C/C++变成可执行文件,需要经历预处理、编译、汇编和链接。
-
常见的Linux系统?
-
单片机发展的阶段?
CVTE笔试
-
排序算法的稳定性
稳定的有,直接插入排序、冒泡排序、归并排序和基数排序。 -
父子进程的关系
- 父进程和子进程有不同的进程控制块。进程控制块PCB是进程存在的唯一标志,每个进程都有自己的PCB。
- 父进程与子进程不能同时使用同一临界资源。
- 父进程与子进程可以并发执行。
- 父子进程没有共享虚拟地址空间。fork()准确来说,是读时共享,写时复制。
一开始时内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进程拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。
注意: fork之后父子进程共享文件,fork产生的子进程与父进程相同的文件描述符指向相同的文件表,引用计数增加,共享文件偏移指针。 -
在局域网中 Ping www.seewo.com 会使用哪些协议?
ICMP、DNS和ARP。
详细过程可以看这个 -
A* pA = NULL;的问题。可以看看这个
class A {
public:
A(int a = 10):a_(a){}
void PrintString() { cout << "balabala"; }
void PrintInt() { cout << a_ << endl; }
private:
int a_;
};
int main()
{
A* pA = NULL;
pA->PrintString(); //这个可以执行
pA->PrintInt(); //执行这个语句就会出错,因为pA指向NULL,并没有a_,所以无法输出。
return 0;
}
-
有10台计算机建成100Mbps以太网,如分别采用共享以太网和交换以太网技术,则每个站点所获得的数据传输速率分另为100Mbps和100Mbps。
-
TCP支持双方同时发送FIN关闭套接字吗?
TCP不支持双方同时发送FIN关闭套接字。在TCP协议中,双方建立连接后,要么主动关闭连接的一方发送FIN报文,要么被动关闭连接的一方发送ACK报文,并且不能同时发送FIN报文。发送FIN报文的一方等待对方的确认(ACK)报文,表示对方已经收到了这个FIN报文,并准备好关闭连接,然后才会发送ACK报文进行最后的确认。因此,在TCP连接的关闭过程中,是有一方先发送FIN报文,等待对方的ACK确认报文,再由对方发送FIN报文,最后由第一方发送ACK进行确认的。
TCL笔试
-
静态数据成员和静态成员函数的目的是什么?静态成员的副作用有哪些?
目的:可以供所有对象共享,另外还节省内存。
副作用:若在一个对象中,它的某个操作修改了某个静态数据成员,然而这一修改使该类所有对象的该静态数据成员都进行了更新,这给程序带来了许多潜在的危险,并且不同对象的修改还会使静态数据成员的值被任意更新,从而导致程序混乱,类的封装性也受到严重破坏。 -
x*=y+8等价于x=x*(y+8)。
注意:在使用 *= 的时候,不需要给操作数加括号。因为 *= 运算的优先级与 * 相同,都高于赋值运算符 =,所以执行顺序是先计算乘法,再执行赋值操作。
举个例子:a *= b + c; 将会先计算 b + c 的值,再用 a 乘上这个值,并将结果赋值给 a。如果想改变执行顺序,可以用括号来调整优先级,比如:a *= (b + c); 就是先计算 b + c,然后再用 a 乘上这个值。 -
不能在类的构造函数中对其静态数据成员进行初始化。
-
参加位运算的数据可以是任何类型的数据,这种说法对吗?
错误。位运算只能用于带符号或无符号的char,short,int与long类型,也就是只能作用于整数对象。在实际应用中,建议用unsigned整型操作数,因为带符号操作数可能因为不同机器结果不同。 -
函数重载是实现静态多态,虚函数实现动态多态。
-
当用public继承从基类派生一个类时,基类的什么成员可被派生类中的成员函数访问?
派生类中的成员函数可以访问基类中的public和protected成员,但不能访问基类的private成员;但派生类对象只能访问基类的public成员。具体来说:-
public成员:派生类中的所有成员函数都可以访问基类中的public成员,这些成员可以被派生类中的任何地方访问。
-
protected成员:派生类中的所有成员函数都可以访问基类中的protected成员,这些成员只能被派生类中的成员函数和派生类的对象访问。
-
private成员:派生类中的所有成员函数都不能访问基类中的private成员,这些成员只能被基类中的成员函数和友元访问。
需要注意的是,派生类中的成员函数可以通过基类的公有和受保护的成员函数访问基类的私有成员。这是因为基类中的公有和受保护的成员函数可以访问基类的私有成员,而派生类可以访问基类的公有和受保护的成员函数。
-
东方海外笔试(时间很短,来不及抄)
-
如果程序需要经常在多种算法中相互替换,应选哪种设计模式?
这种情况下,可以考虑使用策略模式(Strategy Pattern)。该模式定义了一组算法,将每个算法封装到单独的类中,并使它们可以相互替换。这样程序可以在运行时根据需要选择适当的算法。
策略模式的实现需要一个抽象策略类,以及一些具体策略类,每个具体策略类对应一个特定的算法。程序使用时,可以先创建一个具体策略类的对象,然后将其传递给需要使用算法的对象中。由于每个具体策略类都实现了同样的接口,所以可以方便地进行替换。
使用策略模式的好处在于,算法可以独立于客户端程序变化。客户端程序只需要知道如何使用算法,而不需要了解算法的具体实现。同时,如果需要添加或修改算法,也可以通过添加或修改具体策略类来完成,而无需修改客户端程序的代码。
总之,如果程序需要经常在多种算法中相互替换,策略模式是一个不错的设计模式选择。 -
int x = 5, y = 7, z = 9; cout << ((x > y) ? (x > z) ? x : z : (y > z) ? y : z) << endl;
输出结果: 9
过程如下:- 定义了三个变量 x, y, z,并分别赋值为 5,7,9。
- 使用条件运算符 ?: 对 x,y,z 进行比较。
- 首先比较 x 是否大于 y,如果是,则继续比较 x 是否大于 z;如果不是,则继续比较 y 是否大于 z。
- 如果 x 大于 y,且 x 大于 z,则输出 x 的值。
- 如果 x 不大于 y,且 y 大于 z,则输出 y 的值。
- 如果 x 不大于 y,且 y 不大于 z,则输出 z 的值。
- 最终输出的结果为 9。
-
JavaScript中函数具有原型。
-
在 Linux 中,物理内存和虚拟内存的映射关系是多对多的。这是因为在虚拟内存中,每个进程都有自己的地址空间,而这个地址空间是独立的,不同进程的地址空间之间没有直接的映射关系。
同时,物理内存也可以被多个进程共享,这意味着同一块物理内存可以映射到不同的进程地址空间中。这种多对多的映射关系可以提高内存的利用率,减少内存的浪费。
在 Linux 中,虚拟内存和物理内存的映射关系是通过页表来实现的。每个进程都有自己的页表,页表中记录了虚拟地址和物理地址之间的映射关系。当进程访问虚拟地址时,内核会根据页表中的映射关系将虚拟地址转换为物理地址,然后进行实际的访问操作。
总之,在 Linux 中,物理内存和虚拟内存之间的映射关系是多对多的,这种映射关系是由内核动态管理的。 -
对于有一个7个结点的无向图,至少应有多少条边才能确保是连通图,该连通图所有顶点的度数之和为多少
对于一个有 n 个结点的连通无向图,至少需要 n-1 条边才能保证连通。因此,对于有 7 个结点的无向图,至少需要 7-1=6 条边才能确保连通。
对于一个无向图,所有顶点的度数之和等于边的数量的两倍。这是因为每条边都会对两个顶点的度数都产生贡献,因此所有顶点的度数之和就等于边的数量的两倍。
因此,对于这个连通图,它的所有顶点的度数之和应该为2×(n−1),即 2×(7−1)=12。 -
以太网帧的组成
以太网帧(Ethernet Frame)是以太网协议中的数据传输单元,它由以下几个部分组成:-
帧前导码(Preamble):长度为7个字节(56位),用于在传输数据之前进行同步。它的作用是帮助接收方在信号开始时就能识别到稳定的同步位,从而正确解析数据。
-
目的地址(Destination Address):长度为6个字节(48位),表示帧的目的MAC地址,即接收方的物理地址。
-
源地址(Source Address):长度为6个字节(48位),表示帧的源MAC地址,即发送方的物理地址。
-
类型/长度(Type/Length):长度为2个字节(16位),表示数据类型或数据长度,该字段的解释根据实现方式不同而有所差别。
-
数据(Data):长度为46 ~ 1500个字节,表示待传输的数据。
-
帧检验序列(FCS):长度为4个字节(32位),利用CRC算法生成,用于帧的完整性校验,保证数据传输的可靠性。
总之,以太网帧是由前导码、目的地址、源地址、类型/长度、数据和帧检验序列等多个字段组成的,其中每个字段都有其特定的作用,用于确保数据的准确无误地传输。
-