本文主要是介绍后端开发面经系列--360一面面经,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
360嵌入式开发一面面经
公众号:阿Q技术站
来源:
https://www.nowcoder.com/feed/main/detail/22a2f509f0a94c3cbb8ba485de084d19
1、Wi-Fi关联是怎么做的了解过吗?
- 扫描可用网络: 设备会主动扫描附近的无线网络,以查找可用的Wi-Fi网络。这个扫描过程通常包括探测各个Wi-Fi网络的SSID(网络名称)和信号强度。
- 选择网络: 设备会根据一些标准来选择一个要连接的网络,通常是用户事先配置的首选网络,或者信号强度最强的网络。
- 认证: 如果Wi-Fi网络需要密码或其他认证方式(如WPA2密码或WPA3密码),设备将发送认证请求,通常需要提供正确的密码。
- 关联: 一旦认证通过,设备将与Wi-Fi网络关联,建立一个连接。在这个步骤中,设备获取了IP地址,这使得它可以通过网络进行通信。
- DHCP分配: 一旦设备与Wi-Fi网络关联,通常会使用DHCP(Dynamic Host Configuration Protocol)从路由器或DHCP服务器获取IP地址、子网掩码、网关地址和DNS服务器地址等网络配置信息。
- 完成关联: 一旦设备成功获取IP地址和相关网络配置信息,Wi-Fi关联就完成了,设备现在可以通过Wi-Fi网络访问互联网或局域网资源。
2、TCP保证可靠性有哪些措施?
- 序号和确认: TCP 使用序号字段来对数据进行编号,接收方使用确认字段来确认已经收到的数据。这确保了数据的顺序性和完整性。如果发生数据丢失或重传,接收方可以根据序号进行恢复。
- 超时和重传: TCP 使用超时机制来检测丢失的数据包。如果发送方没有收到来自接收方的确认,它会等待一段时间(超时时间),然后重传相应的数据包。这确保了即使数据包在传输过程中丢失,它们最终还是会被正确传输。
- 流量控制: TCP 使用滑动窗口机制来控制数据的发送速率,以防止数据的溢出和拥塞。发送方和接收方之间的窗口大小是动态调整的,以适应网络条件。这有助于避免网络拥塞和数据包丢失。
- 拥塞控制: TCP 使用拥塞窗口来调整数据的发送速率,以避免网络拥塞。拥塞窗口是动态调整的,根据网络拥塞的程度来进行调整。如果网络出现拥塞,发送方会减小发送速率,以减轻网络负担。
- 有限重传次数: TCP 限制了数据包的重传次数,以避免无限重传。如果数据包经过多次重传后仍未成功传输,TCP 将放弃并通知上层应用程序。
- 数据校验: TCP 使用校验和字段来验证数据的完整性。如果数据在传输过程中发生错误,校验和将失败,导致数据被丢弃或重传。
- 连接管理: TCP 使用三次握手和四次挥手的机制来建立和关闭连接。这有助于确保数据的可靠传输,并避免不必要的数据包传输。
3、如何用C语言实现大小端? 什么是大端和小端?
- 小端: 在小端系统中,最低有效字节(即数据的最低位)存储在内存的低地址处,而最高有效字节(最高位)存储在高地址处。这意味着数据的字节从低地址到高地址排列。
- 低地址 ------------------> 高地址
- 0x78 | 0x56 | 0x34 | 0x12
- 大端: 在大端系统中,最高有效字节存储在内存的低地址处,而最低有效字节存储在高地址处。数据的字节从高地址到低地址排列。
- 低地址 -----------------> 高地址
- 0x12 | 0x34 | 0x56 | 0x78
举个例子:
#include <stdio.h>int main() {unsigned int num = 1;unsigned char *byte = (unsigned char *)#printf("Value: %u\n", num);if (*byte == 1) {printf("This system is Little Endian\n");} else {printf("This system is Big Endian\n");}printf("Bytes in memory (from lowest address to highest):\n");for (int i = 0; i < sizeof(num); i++) {printf("%p: %02X\n", (void *)(byte + i), byte[i]);}return 0;
}
检测 byte
的第一个字节的值。如果它是1,则表示小端系统;如果不是1,则表示大端系统。
4、802.11ax和802.11ac/n这些有什么区别? 802.11ax、802.11ac和802.11n是Wi-Fi标准的不同版本,它们在性能、速度、频段和其他方面有一些关键区别。
区别:
- Wi-Fi标准名称和发布年份:
- 802.11n:也称为Wi-Fi 4,于2009年发布。
- 802.11ac:也称为Wi-Fi 5,于2013年发布。
- 802.11ax:也称为Wi-Fi 6,于2019年发布。
- 频段支持:
- 802.11n:工作在2.4GHz和5GHz频段。
- 802.11ac:主要工作在5GHz频段,但也支持2.4GHz。
- 802.11ax:工作在2.4GHz和5GHz频段,提供更好的频谱效率。
- 理论最大速度:
- 802.11n:最高可达300 Mbps或600 Mbps,取决于通道宽度。
- 802.11ac:最高可达1.3 Gbps(1.3 Gigabits per second)。
- 802.11ax:最高可达9.6 Gbps,但实际速度通常更低。
- MIMO技术:
- 802.11n:使用MIMO(多输入多输出)技术,支持多个天线。
- 802.11ac:引入更高级的MIMO,如3x3或4x4 MIMO。
- 802.11ax:引入更高级的MIMO,如8x8 MIMO,并引入了OFDMA技术以提高网络效率。
- 通道宽度:
- 802.11n:支持20MHz和40MHz通道宽度。
- 802.11ac:支持80MHz和160MHz通道宽度。
- 802.11ax:支持更宽的通道宽度,如160MHz和甚至更大的通道宽度。
- 效率和密度:
- 802.11ax引入了更多的效率和密度,支持高密度网络环境,如拥挤的公共场所。
- QAM调制:
- 802.11n:支持16-QAM和64-QAM调制。
- 802.11ac:引入了256-QAM调制。
- 802.11ax:引入了1024-QAM调制。
- 性能和容量:
- 802.11ax旨在提供更高的性能和容量,以应对越来越多的连接设备和应用程序需求。
5、进程和线程的区别?
- 定义:
- 进程:进程是一个独立的执行实体,每个进程都有自己的内存空间、代码、数据和系统资源。
- 线程:线程是进程内的执行单元,多个线程可以共享同一个进程的内存空间和资源,它们在同一个进程中并行执行。
- 独立性:
- 进程:进程是相互独立的,一个进程的崩溃通常不会影响其他进程。
- 线程:线程是进程内的执行单元,它们共享相同的内存空间,因此一个线程的错误可能会影响整个进程,包括其他线程。
- 资源开销:
- 进程:每个进程都有自己的独立内存空间和系统资源,因此创建和销毁进程通常需要较多的系统资源和时间。
- 线程:线程共享同一个进程的资源,因此创建和销毁线程的开销通常较小。
- 通信和同步:
- 进程:不同进程之间的通信通常需要使用进程间通信(IPC)机制,如管道、消息队列、套接字等。
- 线程:线程之间可以通过共享内存等机制更容易地进行通信和同步。
- 切换开销:
- 进程:由于进程切换需要保存和恢复整个进程的状态,进程切换开销较大。
- 线程:线程切换开销较小,因为它们共享相同的地址空间和资源。
- 并发性:
- 进程:进程之间的并发性较低,因为它们是相互独立的。
- 线程:线程之间的并发性较高,因为它们可以在同一个进程中并行执行。
- 适用场景:
- 进程:适用于需要隔离、独立性和安全性的任务,如独立的应用程序。
- 线程:适用于需要高并发性、共享数据和轻量级任务的情况,如多线程编程和服务器应用。
6、多进程编程实现? 多进程编程是指使用多个独立的进程来执行并发任务,通常涉及创建、管理和协调多个进程。在Unix/Linux系统中,可以使用fork()系统调用来创建新的进程,每个进程都有自己的地址空间和代码执行流。
一般步骤:
- 包含必要的头文件: 在C程序中,需要包含
<stdio.h>
和<unistd.h>
头文件以使用fork()函数。 - 使用fork()创建子进程: 使用fork()函数创建一个新的子进程。fork()会复制父进程的地址空间和执行状态,包括程序代码、数据和文件描述符等。
pid_t child_pid = fork();
-
检查fork()的返回值:
-
如果fork()返回负值,表示创建子进程失败。
-
如果fork()返回0,表示当前代码正在子进程中执行。
-
如果fork()返回正值,表示当前代码正在父进程中执行,返回值是子进程的PID(进程标识符)。
-
-
在父子进程中执行不同的任务: 在父子进程中,可以编写不同的代码来执行不同的任务。通常,父进程会等待子进程完成,或者父子进程可以进行进一步的通信和协调。
-
子进程的任务完成后退出: 在子进程完成其任务后,可以使用exit()函数退出子进程。
if (child_pid == 0) {// 子进程执行的代码// ...exit(0);
}
- 父进程等待子进程完成: 父进程可以使用wait()或waitpid()等系统调用来等待子进程的完成,并获取子进程的退出状态。
if (child_pid > 0) {int status;wait(&status);// 在这里可以处理子进程的退出状态
}
7、MCS是什么(没料到嵌入式还问物理层)?
“MCS” 通常代表 “Modulation and Coding Scheme”(调制与编码方案),它是一种用于描述在无线通信中如何调制和编码数据的方法或方案。 MCS 通常与 Wi-Fi、无线局域网(Wireless LAN)、LTE、5G 和其他无线通信技术相关。
- 调制方式: 在传输数据时使用的调制方式,包括调制类型(例如,QPSK、16-QAM、64-QAM)和符号率。不同的调制方式具有不同的数据传输速率和容忍度。
- 编码方式: 数据在传输前如何编码以提高错误容忍度。通常,更高级别的编码可以提供更好的容错性,但以牺牲传输速率为代价。
MCS 的选择取决于通信环境、信道质量、带宽和应用需求。通常,较差的信道质量需要更低级别的 MCS 以提供更好的容错性,而较好的信道质量则可以使用更高级别的 MCS 以获得更高的数据传输速率。
8、C语言中如何防止同一个.h文件被重复 #include?
在C语言中,可以使用预处理指令和条件编译来防止同一个头文件被重复包含。这通常通过宏和条件编译指令来完成。
给个参考方法:
- 在头文件(.h文件)的开头添加条件编译宏,以确保头文件只会被包含一次:
#ifndef HEADER_NAME_H
#define HEADER_NAME_H// 头文件的内容#endif
HEADER_NAME_H
是一个自定义的宏名称,通常采用头文件名的大写形式。你可以用不同的名字替换它,只要确保它在头文件的不同地方是唯一的。
-
当预处理器首次遇到
#ifndef
指令时,会检查是否定义了HEADER_NAME_H
这个宏。如果没有定义,它会执行#define
部分,将HEADER_NAME_H
定义为一个值,然后继续处理头文件。 -
当同一个头文件再次被包含时,预处理器会检测到
HEADER_NAME_H
已经被定义,因此不会再次包含头文件的内容。
这样,就确保同一个头文件不会被重复包含,防止出现重复定义的问题。
例子:
#ifndef MYHEADER_H
#define MYHEADER_H#include <stdio.h>void myFunction();#endif
在使用这个头文件的源文件中,只需要包含一次该头文件,无需担心重复包含:
#include "myheader.h"int main() {myFunction();return 0;
}
9、动态链接和静态链接分别是什么?
- 静态链接(Static Linking):
- 工作方式:在静态链接中,所有所需的库和模块在编译时都会被合并到最终的可执行文件中。这意味着可执行文件包含了所有依赖的代码和数据,形成一个独立的、自包含的文件。
- 优点:
- 可执行文件独立,不依赖外部库或模块,因此更容易分发和部署。
- 静态链接在编译时进行,可以在编译器检查到错误和优化代码。
- 程序运行时没有外部依赖,因此通常更稳定。
- 缺点:
- 可执行文件较大,因为它包含了所有依赖的代码,可能占用更多的磁盘空间。
- 每次更新代码或库时,必须重新编译整个程序。
- 动态链接(Dynamic Linking):
- 工作方式:在动态链接中,程序的不同部分被分为可执行文件和动态链接库(DLL)两部分。可执行文件包含程序的核心代码,但依赖的库被保存在独立的DLL文件中。这些库在程序运行时动态加载和链接。
- 优点:
- 节省磁盘空间,因为多个程序可以共享相同的动态链接库,减少了重复存储。
- 更容易更新库,因为只需替换DLL而不需要重新编译整个程序。
- 缺点:
- 需要运行时动态加载和链接,可能引入一些性能开销。
- 可能出现版本不一致的问题,因为不同程序可能依赖于不同版本的库。
通常,动态链接在现代操作系统中更为普遍,因为它可以节省磁盘空间并使软件更新更容易。静态链接通常用于嵌入式系统或需要独立、自包含可执行文件的情况。在某些情况下,可以使用混合链接,将一些库静态链接到可执行文件中,而将其他库动态链接以在需要时加载。
10、对指针的理解?
- 指针是内存地址的容器: 指针是一个变量,它存储了内存中某个数据的地址,而不是数据本身。这使得程序可以引用和操作内存中的数据,而不需要知道数据的确切值或位置。
- 数据类型相关: 指针的数据类型与它所指向的数据的数据类型相关。这意味着指针的类型决定了指针可以指向哪种类型的数据。例如,一个整数指针可以指向整数变量,一个字符指针可以指向字符数组。
- 指针操作: 可以使用指针来访问、修改和操作内存中的数据。通过指针,可以读取或写入数据,还可以执行指针算术操作,如递增和递减指针,以遍历数组或数据结构。
- 空指针: 空指针是指不指向任何有效内存地址的指针。在C/C++中,空指针通常用
NULL
(C)或nullptr
(C++)表示。 - 野指针: 野指针是指指向未知或无效内存地址的指针,访问野指针可能导致未定义行为或程序崩溃。
- 指针和数组: 数组名本质上是一个指向数组首元素的指针,因此可以通过指针操作来访问数组元素。例如,
arr[0]
可以写为*arr
。 - 指针和函数: 指针可以用于传递函数参数,允许函数修改调用者的变量或在函数间共享数据。
- 动态内存分配: 指针常用于动态内存分配,如在C/C++中使用
malloc
和free
分配和释放内存。 - 引用和指针: 指针和引用(在C++中)都用于处理内存中的数据,但它们有不同的语法和语义。引用是指已有变量的别名,而指针是一个独立的变量,存储了另一个变量的地址。
11、函数指针怎么使用 有什么作用? 函数指针是指向函数的指针变量,它允许程序在运行时动态选择要调用的函数。
函数指针的声明和定义:
return_type (*function_pointer)(parameter_list);
return_type
是函数的返回类型。function_pointer
是函数指针变量的名称。parameter_list
是函数的参数列表。
函数指针的初始化:
函数指针可以初始化为指向具体函数的地址。
int (*add)(int, int) = &addition_function;
将 add
函数指针初始化为指向名为 addition_function
的函数的地址。
使用函数指针调用函数:
函数指针可以像调用普通函数一样使用,通过使用函数调用操作符 ()
。
int result = (*add)(3, 4); // 调用addition_function(3, 4)
或者可以简化为:
int result = add(3, 4);
回调函数:
函数指针常用于实现回调函数的机制。通过将函数指针传递给其他函数,可以使其他函数在适当的时候调用指定的函数,以执行特定的操作。这在事件处理、异步编程等方面非常有用。
动态算法选择:
函数指针允许在运行时选择要执行的算法。这对于根据不同条件选择不同的计算方法非常有用。例如,可以根据用户输入选择不同的排序算法。
函数表:
函数指针可以用于创建函数表,函数表是一个数组,每个元素都是一个函数指针,用于执行不同的操作。程序可以根据索引或条件来选择要执行的函数。
12、TCP/IP网络模型一共几层 哪几层?
- 应用层: 应用层是网络协议栈的最顶层,包括了应用层协议,如HTTP、FTP、SMTP、DNS等。这些协议负责应用程序之间的通信和数据交换。
- 传输层: 传输层提供端到端的数据传输和错误检测。最常见的传输层协议是TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供可靠的、面向连接的数据传输,而UDP提供不可靠的、面向无连接的数据传输。
- 网络层: 网络层处理数据包的路由和转发。主要协议包括IP(Internet协议),它负责数据包的寻址和路由。IPv4和IPv6是两个常见的IP版本。
- 数据链路层: 数据链路层负责物理介质的访问和数据帧的传输。它通常包括以太网、Wi-Fi、PPP等协议,用于在相邻节点之间传输数据帧。
13、ARP协议具体干什么的?
ARP(Address Resolution Protocol,地址解析协议)是用于将网络层(如IPv4)的IP地址映射到数据链路层(如以太网)的物理硬件地址(MAC地址)的协议。
几个功能:
- IP地址到MAC地址的映射: ARP用于确定本地网络上的特定IP地址对应的MAC地址。这是因为在一个局域网中,通信设备(如计算机、路由器)通过MAC地址来识别和定位其他设备,而不是IP地址。
- ARP请求和应答: 当设备需要将某个IP地址映射到MAC地址时,它会发送一个ARP请求广播到局域网中的所有设备。带有请求IP地址的设备会响应ARP请求,提供与该IP地址相关联的MAC地址。这个过程通常包括ARP请求和ARP应答两个步骤。
- ARP缓存: 设备通常会维护一个ARP缓存表,其中存储了最近已解析的IP地址和相应的MAC地址。这样,在未来的通信中,设备可以直接查找ARP缓存,而不必每次都发送ARP请求。
- ARP攻击检测: ARP协议还涉及到检测和防范ARP欺骗攻击,其中攻击者尝试伪装成其他设备,欺骗网络中的设备以获得通信优势。
这篇关于后端开发面经系列--360一面面经的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!