腾讯面试准备

算法和数据结构

刷题

C++的stl和新特性

常量指针和指针常量

1
const int *p = &a;//常量指针,指针指向常量,指针指向的变量不能通过指针修改,但是指针指向的值可以改变
1
int *const p = &a;//指针常量,是一个常量,不能改变指向的对象(地址),但是可以改变地址的内容

野指针和悬浮指针

野指针是指向已经被释放或者⽆效的内存地址的指针

悬浮指针是指向已经被销毁的对象的引⽤

区别就是一个是指针一个是引用

网络编程

网络编程的基本流程

(1) 服务端代码流程

1
2
3
4
5
6
1. socket()             // 创建一个套接字
2. bind() // 将套接字与特定 IP 地址和端口绑定
3. listen() // 开启监听,等待客户端连接
4. accept() // 相应客户端的连接请求,接受新的连接
5. send()/recv() // 用于和客户端通信(读取请求或者写入响应
6. close() // 关闭连接

(2) 客户端代码流程

1
2
3
4
1. socket()             // 创建一个套接字
2. connect() // 连接到服务端指定的 IP 地址和端口
3. send()/recv() // 用于发送请求或读取服务端的响应
4. close() // 关闭连接

调试命令,gdb/vscode

额外:linux操作系统的内存管理,文件系统,进程和线程调度

进程间的通信机制:信号量,条件变量,生产者消费者

异步通信:回调函数,生产者消费者(promise-future和消息队列)

同步通信:阻塞式调用,文件或网络操作

引用和指针的区别

1. 什么是引用和指针?

  1. 引用(Reference)

    • 引用是某个变量的别名,声明后与该变量绑定在一起,不能再绑定其他变量。
    • 本质上是一个语法糖,用更简洁的方式访问变量。

    示例代码:

    1
    2
    3
    int a = 10;
    int& ref = a; // 引用 `ref` 绑定到变量 `a`
    ref = 20; // 实际修改的是 `a`
  2. 指针(Pointer)

    • 指针是存储变量地址的一种特殊变量,通过指针可以间接访问或操作存储在内存地址上的值。
    • 指针可以指向不同的变量或内存单元。

    示例代码:

    1
    2
    3
    int a = 10;
    int* ptr = &a; // 指针 ptr 存储变量 a 的地址
    *ptr = 20; // 实际修改的是 a

2. 引用和指针的区别

特性 引用 指针
定义方式 使用 & 声明 使用 * 声明
是否可以为空 引用必须绑定到变量 指针可以是 nullptr 或空
绑定后是否可以更改 一旦绑定不能更改 指针可以重新指向其他变量
语法 直接使用,无需解引用符号 需要用 * 解引用
内存布局 编译器实现(可能是指针) 明确存储变量地址
需要初始化 声明时必须初始化 可以声明后再初始化
别名关系 是原变量的别名 独立的变量
运算 不支持运算 可以进行加减运算
灵活性 较低,限定性强 较高,可以动态分配内存

3. 区别详解

(1) 是否必须初始化

  • 引用:

    • 引用在声明的时候必须初始化,否则无法通过编译。
    1
    2
    int a = 10;
    int& ref; // 编译错误,引用必须绑定变量
  • 指针:

    • 指针声明后可以不初始化,但最好将其初始化(例如初始化为 nullptr),否则容易产生未定义行为。
    1
    2
    int* ptr;  // 未初始化,非法操作可能导致未定义行为
    int* ptr = nullptr; // 推荐初始化为 nullptr

(2) 是否可以为空

  • 引用:

    • 引用不能指向空(nullptr),它必须始终绑定到有效的变量。
  • 指针:

    • 指针可以指向空内存区域(nullptr),表示它当前没有指向任何变量。

    示例:

    1
    2
    int* ptr = nullptr;  // 合法
    int& ref = nullptr; // 编译错误,引用必须绑定到变量

(3) 绑定后是否可以更改

  • 引用:

    • 引用一旦绑定到变量,就不能再绑定到其他变量,引用始终是它所绑定变量的别名。
    1
    2
    3
    int a = 10, b = 20;
    int& ref = a; // ref 绑定到 a
    ref = b; // 修改的是 a 的值,而不是重新绑定到 b
  • 指针:

    • 指针可以随时更改指向,可以指向其他变量或内存单元。
    1
    2
    3
    int a = 10, b = 20;
    int* ptr = &a; // ptr 指向 a
    ptr = &b; // ptr 改为指向 b

(4) 使用上的差异

  • 引用:

    • 如果将一变量赋值给引用,引用直接操作变量本身。
    1
    2
    3
    int a = 10;
    int& ref = a;
    ref = 20; // 改变的是 a,a 的值变为 20
  • 指针:

    • 要改变指针指向变量的值,需要解引用(*)。
    1
    2
    3
    4
    int a = 10, b = 20;
    int* ptr = &a; // 指针指向 a
    *ptr = 30; // 修改 a 的值为 30
    ptr = &b; // 改为指向 b

(5) 运算能力

  • 引用:
    • 引用不支持指针的算术运算,例如加减法。
  • 指针:
    • 指针可以进行算术运算,例如指针递增/递减,用于访问数组元素。
    1
    2
    3
    int arr[3] = {1, 2, 3};
    int* ptr = arr; // 指向数组的第一个元素
    ptr++; // 指向下一个元素

(6) 内存特性

  • 引用在编译器实现中,可能会转换为指针,但它对开发者是透明的。
  • 指针本质上是一个变量,存储的是某个地址,并且占用内存。

4. 引用与指针的适用场景

(1) 适用引用

  1. 函数参数传递

    • 使用引用避免拷贝实参,提高性能。
    • 常用于不需要修改参数的地方(const 引用)。
    1
    2
    3
    void print(const int& x) {
    std::cout << x << std::endl;
    }
  2. 返回值

    • 返回引用允许函数直接返回变量本身,而不是拷贝。
    1
    2
    3
    int& getValue(int& a) {
    return a;
    }
  3. 别名

    • 为变量创建更简化的别名。
    1
    2
    int a = 42;
    int& alias = a; // alias 是 a 的别名

(2) 适用指针

  1. 动态内存管理

    • 使用指针分配和释放动态内存。
    1
    2
    int* ptr = new int(10);
    delete ptr; // 手动释放内存
  2. 数组与迭代

    • 指针常用来访问数组元素。
    1
    2
    3
    4
    int arr[3] = {1, 2, 3};
    for (int* ptr = arr; ptr < arr + 3; ++ptr) {
    std::cout << *ptr << " ";
    }
  3. 数据结构

    • 指针是数据结构(如链表、树)的核心基础。
    1
    2
    3
    4
    struct Node {
    int data;
    Node* next;
    };
  4. 需要动态改变指向时

    • 指针可以灵活地改变指向,适合需要频繁切换指向的场景。

5. 总结对比

特征 引用 指针
需要初始化 必须初始化 可以先声明后初始化
是否可以为空 不可以为空 可以是空指针(nullptr
绑定是否可更改 绑定后不可更改 可通过重新赋值更改指向
语法复杂度 更简单 更复杂,需要使用 *
灵活性 受限 更灵活
常见场景 函数传参、别名 动态内存、复杂结构

引用更简单、更安全,适合大多数普通变量操作;指针更灵活,适合动态内存和复杂结构的场景。在实际开发中,应根据场景和需求选择适合的工具。