我们来逐个回答这些问题,便于你理解:

2. staticconst

  • **static**:在C++中,static关键字可以有不同的含义:

    • 在函数内部:用于声明静态变量,使其在函数调用结束后依旧保留状态。
    • 在类中:用于声明类的静态成员变量或静态成员函数。静态成员属于类而非对象,所有对象共享。
    • 在文件作用域:将函数或变量声明为静态可以限制其作用域,只在当前文件中可见。
  • **const**:const关键字用于定义常量或不可修改的变量。const修饰的变量必须在声明时初始化,且不可更改。常见的用法包括:

    • 修饰指针,使指针内容不可更改或指针本身不可更改(const int* pint* const p)。
    • 修饰类成员函数,使其不可修改类成员数据,适用于只读访问。

3. 指针数组 和 数组指针

  • 指针数组:即存储指针的数组,数组的每个元素都是指向某种数据类型的指针。例如,int* arr[10]是一个指针数组,包含10个指向int的指针。
  • 数组指针:即指向数组的指针,指针指向一个数组的起始地址。例如,int (*p)[10]是一个数组指针,指向一个包含10个int元素的数组。

4. 线程池(使用时的注意事项)

线程池用于管理多个线程的创建和销毁,避免线程频繁创建带来的开销。使用线程池时需要注意:

  • 任务划分:确保任务独立、无依赖,避免线程竞争。
  • 线程数量:根据CPU核心数和任务类型决定线程池大小,避免创建过多线程。
  • 异常处理:确保线程中的异常被捕获,否则会导致线程崩溃或未完成任务。
  • 资源回收:线程池应具备合理的线程回收机制,避免资源泄漏。

5. TCP 和 UDP 的区别

  • TCP(Transmission Control Protocol):一种面向连接的可靠传输协议,数据传输之前需要建立连接,保证数据的有序和准确到达。
  • UDP(User Datagram Protocol):一种无连接的传输协议,不保证数据到达顺序和准确性,传输速度较快,适合实时应用如视频流和游戏。

6. TCP 三次握手 (服务器端 SYN.ACK 丢失处理)

TCP三次握手:

  1. 客户端发送SYN包请求连接。
  2. 服务器端收到后返回SYN.ACK确认连接。
  3. 客户端再发送ACK确认包,连接建立。

若服务器端的SYN.ACK丢失,客户端会因等待超时重新发送SYN,触发服务器端重新回复SYN.ACK。

7. TCP 如何保证数据传输的准确性,机制有哪些

TCP提供可靠传输的机制包括:

  • 序列号:每个包都有唯一序号,接收方可以按序组装数据并检测丢失。
  • 确认应答:接收方需确认收到数据包,未确认的数据包会重发。
  • 流量控制:接收方可以通知发送方其接收能力,避免发送过多数据。
  • 拥塞控制:通过调整发送速度,避免网络拥堵。
  • 检验和:每个数据包有校验码,用于检测传输中的数据损坏。

8. 如何理解 TCP 是面向连接的基于字节流的协议,UDP 是面向无连接的基于数据报的协议

  • TCP:面向连接的字节流协议,即数据在连接建立后以字节流形式连续传输。TCP需建立连接(如三次握手),适合顺序性和准确性要求高的传输。
  • UDP:无连接的数据报协议,每个数据包独立传输,不依赖先前包。无需建立连接,适合实时性要求高的应用。

9. IPC(进程间通信)

IPC指多个进程之间数据交换和通信的方法,常见的IPC机制包括:

  • 管道有名管道:用于单向或双向进程间数据传输。
  • 信号量:用于进程间同步和资源控制。
  • 共享内存:多个进程直接访问内存区域,速度快。
  • 消息队列:进程通过消息发送接收数据。
  • 套接字:通过网络实现本地或分布式进程通信。

10. 有名管道(FIFO)的缺点

有名管道的缺点主要有:

  • 只能在同一主机上通信,不能跨主机。
  • 半双工通信:数据只能单向传输,需建立两个管道实现双向通信。
  • 阻塞读写:读进程或写进程可能会因为另一方未准备好而阻塞。

12. Shell 的 for 循环

Shell的for循环用于遍历列表中的每个元素:

1
2
3
4
for var in item1 item2 item3
do
# 执行的命令
done

或用于遍历文件、命令输出:

1
2
3
4
for var in $(ls)
do
echo "$var"
done

C和C++的区别

  1. C面向对象C++面向过程
  2. C++具有封装、继承、多态的性质
  3. C++具有STL

#define和const的区别

  1. 一个发生在预处理阶段,一个发生在编译阶段
  2. #define只是简单的字符串替换没有进行类型的安全检查,const进行类型的安全检查
  3. #define的字符串会替换多份,更占用内存,const常量只保存一份(保存在常量区)

程序编译的过程

预处理:头文件展开,宏替换,条件编译,去注释
编译:语义分析,语法分析,目标代码生成,优化,生成汇编代码
汇编:生成二进制文件
链接:将目标文件进行链接形成可执行程序(解决符号表的问题)

内存的布局

从高地址到低地址(自顶向下),栈区,共享区,堆区,代码段,数据段

进程间的通信方式

管道,消息队列,共享内存,信号量,Socket网络通信

TCP拥塞控制机制

  1. 慢启动 因为TCP链接刚建立之后不确定网络的负荷情况,因此,较好的方式就是先探测一下,即由小到大逐渐增大发送窗口,
    通常在刚开始发送报文的时候,先把拥塞窗口的大小设置为一个最大报文段MSS的数值,而在每收到一个对新的报文的确认后,
    就把拥塞窗口增加至多一个MSS的数值。(注意:因为是每一个ACK都会让MSS加1,所以cwnd = cwnd*2)
    为了防止拥塞窗口的cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量,当cwnd >= ssthresh的时候,就会进入“拥塞避免算法”
  2. 拥塞避免 避免增长过快导致网络拥塞,让拥塞窗口cwnd缓慢的增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd整体加上1,而不是加倍。
    注意:无论是在满开始阶段还是拥塞避免阶段,只要发送方没有接收到确认信息,那么就是出现了网络拥塞,
    则需要将慢开始门限设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口的大小重新设置为1,执行慢开始算法。
  3. 快重传 发送方连续收到三次重复的确认就应当立即重传对方尚未收到的报文
  4. 快恢复 当发送方连续收到三个重复的确认,把慢开始门限设置为此时拥塞窗口值的一半
    由于发送方现在认为网络很可能没有发生拥塞,则此时不执行慢开始算法,而是将cwnd的大小设置为减半后的慢开始门限sstream的数值,开始执行拥塞避免算法。

面向对象和面向过程的思想有什么区别

  1. 设计思想 面向过程:以过程(步骤)为核心,将整个程序逻辑按功能划分成一系列函数;面向对象:以对象和类为核心,将数据和行为封装到对象中,并通过对象的相互作用实现功能
  2. 代码组织结构 面向过程:数据和操作是分开的;面向对象:数据和操作封装在对象中,类是对象的模板
  3. 重用性与维护性 面向过程:POP主要通过函数的重用来实现代码重用;面向对象:OOP的类和对象封装实现了更高的代码复用性

进程的几大状态

进程在操作系统中通常会经历几种基本状态,每种状态代表了进程当前的活动情况。常见的进程状态主要有以下几种:

  1. 新建(New)
    进程刚被创建,还未被系统调度到执行的状态。

  2. 就绪(Ready)
    进程已准备好执行,但由于没有空闲的CPU,暂时等待被调度器分配资源。此状态的进程具备执行条件,只需等待分配CPU即可运行。

  3. 运行(Running)
    进程获得了CPU资源,正在执行状态。单核处理器上通常只有一个进程处于“运行”状态,而多核处理器上可以同时运行多个进程。

  4. 阻塞(Blocked)/等待(Waiting)
    进程正在等待某种事件的完成(如I/O操作、获取锁等),暂时无法继续执行。当所等待的事件发生时,进程会从阻塞状态转为就绪状态。

  5. 终止(Terminated)/退出(Exit)
    进程执行完毕,或由于错误而被强制结束,此时进程处于“终止”状态。系统会回收资源,等待其他进程来使用。

  6. 挂起(Suspended)
    某些操作系统中还存在“挂起”状态,分为就绪挂起阻塞挂起。挂起状态下的进程被暂时移出主存,以便腾出更多内存资源,但挂起进程仍保留运行信息,以备恢复。

状态切换

进程在运行过程中会在上述几种状态之间进行转换。例如:

  • 就绪 → 运行:当进程被调度到CPU时。
  • 运行 → 阻塞:进程需要等待I/O操作完成时。
  • 阻塞 → 就绪:等待事件完成时,进程重新进入就绪状态。
  • 运行 → 就绪:当进程被剥夺CPU资源(例如时间片耗尽)时。

客户端发送 FIN 后是否能继续发送数据

客户端发送FIN信号后,表示请求终止连接。此时,连接进入半关闭状态,服务器仍可以向客户端发送数据,
但客户端已无法继续发送新数据,只能接收服务器发送的数据。双方均发送FIN信号后,连接完全关闭。

HTTP和HTTPS的区别

数据加密:HTTPS使用TLS(传输层安全协议)或SSL(安全套接层协议)加密数据传输,HTTP则是明文传输,不安全。
端口号:HTTP默认使用端口80,HTTPS默认使用端口443。
安全性:HTTPS通过加密和身份验证防止中间人攻击、窃听、篡改,适合用于传输敏感信息,而HTTP缺乏此安全保护。
证书验证:HTTPS需要数字证书(如SSL证书),用于确认服务器身份,HTTP则不需要证书。

GET和POST的区别

请求参数传递方式:GET将参数直接附在URL后,通过URL传递参数;POST则通过请求体传递参数。
安全性:GET请求的参数会显示在URL中,敏感信息易泄露;POST将参数放在请求体中,相对更安全。
数据长度限制:GET请求的URL长度受限制(通常为2048字节),而POST理论上没有数据长度限制,适合传输大数据。
幂等性:GET请求通常为幂等的,即多次执行不会对服务器数据产生影响,而POST请求可能会对数据产生修改,因此非幂等。
用途:GET主要用于获取数据,而POST主要用于提交数据或进行修改操作。

进程和线程能否共享同一片内存

进程之间不能直接共享内存空间,它们各自拥有独立的地址空间。
若需要通信,可以使用进程间通信(IPC)机制,如管道、共享内存、消息队列等。

线程属于同一进程,多个线程可以共享进程的全局变量、堆内存等。因此,线程可以共享同一片内存,
这也是多线程数据同步需要特别注意的原因,以防止数据竞争。

TCP和UDP的区别

连接方式:TCP是面向连接的协议,数据传输前需要三次握手建立连接;UDP是无连接的协议,数据直接传输,不需要建立连接。
传输可靠性:TCP提供可靠的数据传输,有确认机制、重传机制、流量控制、拥塞控制,保证数据完整性;UDP不保证传输可靠性,数据可能丢失、乱序。
速度:TCP因连接、确认等机制导致传输速度较慢,适合可靠性要求高的应用;UDP无连接,传输速度较快,适合实时性要求高的应用。
数据流和数据报:TCP是基于字节流的协议,将数据按字节流传输,适合大数据传输;UDP是基于数据报的协议,每次传输一个完整数据报,适合传输小数据量的场景。
应用场景:TCP适合文件传输、电子邮件、网页加载等场景;UDP适合实时视频、语音通话、在线游戏等对时延要求高的场景。

懒汉模式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <mutex>

class Singleton {
public:
// 静态方法,用于获取单例实例
static Singleton* getInstance() {
// 双重检查锁,确保线程安全的同时减少不必要的加锁
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mutex_);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}

// 删除拷贝构造和赋值操作符,防止对象被复制
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

void displayMessage() const {
std::cout << "Hello from Singleton instance!" << std::endl;
}

private:
Singleton() = default; // 构造函数私有化,确保只能内部创建实例
static Singleton* instance;
static std::mutex mutex_;
};

// 静态成员变量的初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;

int main() {
Singleton* singletonInstance = Singleton::getInstance();
singletonInstance->displayMessage();

return 0;
}

快速排序

给你一个整数数组 nums,请你将该数组升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
srand(time(NULL));
qsort(nums,0,nums.size()-1);
return nums;
}

void qsort(vector<int>& nums,int left,int right)
{
if(left>=right) return;
int key=get_rand(nums,left,right);
int i=left,l=left-1,r=right+1;
while(i<r)
{
if(nums[i]<key) swap(nums[++l],nums[i++]);
else if(key==nums[i]) i++;
else swap(nums[i],nums[--r]);
}

//[left,l][l+1,r-1][r,right]
qsort(nums,left,l);
qsort(nums,r,right);
}

int get_rand(vector<int>& nums,int left,int right)
{
int r=rand();
return nums[r%(right-left+1)+left];
}
};

堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void Adjustdown(vector<int>& arr, int i){
int n=arr.size();
for(int j=2*i+1;j<n;j=2*j+1){
if(j+1<n&&arr[j+1]<arr[j]) j++;
if(arr[j]<arr[i]) {
swap(arr[j],arr[i]);
i=j;
}
else break;
}
}
void Heapsort(vector<int>& arr){
int n=arr.size();
for(int i=(n-1-1)/2;i>=0;i--){
Adjustdown(arr,i);
}

for(int i=n-1;i>=0;i--){
swap(arr[0],arr[i]);
Adjustdown(arr,0);
}
}
int main() {
vector<int> arr{1,3,5,7,9,2,4,6,8,0};
Heapsort(arr);
for(auto i:arr) cout<<i<<" ";
return 0;
}

约瑟夫环问题

社团共有 num 位成员参与破冰游戏,编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。
社长抽取一个数字 target,从 0 号成员起开始计数,排在第 target 位的成员离开圆桌,
且成员离开后从下一个成员开始计数。请返回游戏结束时最后一位成员的编号。

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
int iceBreakingGame(int num, int target) {
int x=0;
for(int i=2;i<=num;i++){
x=(x+target)%i;
}
return x;
}
};

大小端

1
2
3
4
5
6
7
8
9
10
11
12
int main(){
int a=1;
char* p=(char*)&a;
if(*p){
cout<<"小端"<<endl;
}
else{
cout<<"大端"<<endl;
}
return 0;
}

查看文件权限 & 区分文件和目录

1
ls -l

-表示普通文件,d 表示目录

Socket 通信服务器创建步骤及函数

  1. 创建套接字:socket() 函数创建一个套接字,返回一个文件描述符

    1
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  2. 绑定 IP 地址和端口:bind() 函数绑定套接字到指定的 IP 地址和端口

    1
    2
    3
    4
    5
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);
    bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  3. 监听连接:listen() 函数将套接字设为监听模式,等待客户端连接

    1
    listen(sockfd, backlog);
  4. 接受连接:accept() 函数接受客户端连接,返回一个新的文件描述符用于后续通信

    1
    int new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);

Linux 基本指令 - vim 显示行号 & 跳转行

显示行号:在 vim 中打开文件后,输入 :set number 或 :set nu,行号会显示在左侧。
跳转到第十行:在正常模式下,直接输入 10G,光标会跳到第10行。