腾讯面试准备
算法和数据结构
刷题
C++的stl和新特性
常量指针和指针常量
1 | const int *p = &a;//常量指针,指针指向常量,指针指向的变量不能通过指针修改,但是指针指向的值可以改变 |
1 | int *const p = &a;//指针常量,是一个常量,不能改变指向的对象(地址),但是可以改变地址的内容 |
野指针和悬浮指针
野指针是指向已经被释放或者⽆效的内存地址的指针
悬浮指针是指向已经被销毁的对象的引⽤
区别就是一个是指针一个是引用
网络编程
网络编程的基本流程
(1) 服务端代码流程
1 | 1. socket() // 创建一个套接字 |
(2) 客户端代码流程
1 | 1. socket() // 创建一个套接字 |
调试命令,gdb/vscode
额外:linux操作系统的内存管理,文件系统,进程和线程调度
进程间的通信机制:信号量,条件变量,生产者消费者
异步通信:回调函数,生产者消费者(promise-future和消息队列)
同步通信:阻塞式调用,文件或网络操作
引用和指针的区别
1. 什么是引用和指针?
引用(Reference):
- 引用是某个变量的别名,声明后与该变量绑定在一起,不能再绑定其他变量。
- 本质上是一个语法糖,用更简洁的方式访问变量。
示例代码:
1
2
3int a = 10;
int& ref = a; // 引用 `ref` 绑定到变量 `a`
ref = 20; // 实际修改的是 `a`指针(Pointer):
- 指针是存储变量地址的一种特殊变量,通过指针可以间接访问或操作存储在内存地址上的值。
- 指针可以指向不同的变量或内存单元。
示例代码:
1
2
3int a = 10;
int* ptr = &a; // 指针 ptr 存储变量 a 的地址
*ptr = 20; // 实际修改的是 a
2. 引用和指针的区别
| 特性 | 引用 | 指针 |
|---|---|---|
| 定义方式 | 使用 & 声明 |
使用 * 声明 |
| 是否可以为空 | 引用必须绑定到变量 | 指针可以是 nullptr 或空 |
| 绑定后是否可以更改 | 一旦绑定不能更改 | 指针可以重新指向其他变量 |
| 语法 | 直接使用,无需解引用符号 | 需要用 * 解引用 |
| 内存布局 | 编译器实现(可能是指针) | 明确存储变量地址 |
| 需要初始化 | 声明时必须初始化 | 可以声明后再初始化 |
| 别名关系 | 是原变量的别名 | 独立的变量 |
| 运算 | 不支持运算 | 可以进行加减运算 |
| 灵活性 | 较低,限定性强 | 较高,可以动态分配内存 |
3. 区别详解
(1) 是否必须初始化
引用:
- 引用在声明的时候必须初始化,否则无法通过编译。
1
2int a = 10;
int& ref; // 编译错误,引用必须绑定变量指针:
- 指针声明后可以不初始化,但最好将其初始化(例如初始化为
nullptr),否则容易产生未定义行为。
1
2int* ptr; // 未初始化,非法操作可能导致未定义行为
int* ptr = nullptr; // 推荐初始化为 nullptr- 指针声明后可以不初始化,但最好将其初始化(例如初始化为
(2) 是否可以为空
引用:
- 引用不能指向空(
nullptr),它必须始终绑定到有效的变量。
- 引用不能指向空(
指针:
- 指针可以指向空内存区域(
nullptr),表示它当前没有指向任何变量。
示例:
1
2int* ptr = nullptr; // 合法
int& ref = nullptr; // 编译错误,引用必须绑定到变量- 指针可以指向空内存区域(
(3) 绑定后是否可以更改
引用:
- 引用一旦绑定到变量,就不能再绑定到其他变量,引用始终是它所绑定变量的别名。
1
2
3int a = 10, b = 20;
int& ref = a; // ref 绑定到 a
ref = b; // 修改的是 a 的值,而不是重新绑定到 b指针:
- 指针可以随时更改指向,可以指向其他变量或内存单元。
1
2
3int a = 10, b = 20;
int* ptr = &a; // ptr 指向 a
ptr = &b; // ptr 改为指向 b
(4) 使用上的差异
引用:
- 如果将一变量赋值给引用,引用直接操作变量本身。
1
2
3int a = 10;
int& ref = a;
ref = 20; // 改变的是 a,a 的值变为 20指针:
- 要改变指针指向变量的值,需要解引用(
*)。
1
2
3
4int a = 10, b = 20;
int* ptr = &a; // 指针指向 a
*ptr = 30; // 修改 a 的值为 30
ptr = &b; // 改为指向 b- 要改变指针指向变量的值,需要解引用(
(5) 运算能力
- 引用:
- 引用不支持指针的算术运算,例如加减法。
- 指针:
- 指针可以进行算术运算,例如指针递增/递减,用于访问数组元素。
1
2
3int arr[3] = {1, 2, 3};
int* ptr = arr; // 指向数组的第一个元素
ptr++; // 指向下一个元素
(6) 内存特性
- 引用在编译器实现中,可能会转换为指针,但它对开发者是透明的。
- 指针本质上是一个变量,存储的是某个地址,并且占用内存。
4. 引用与指针的适用场景
(1) 适用引用
函数参数传递:
- 使用引用避免拷贝实参,提高性能。
- 常用于不需要修改参数的地方(
const引用)。
1
2
3void print(const int& x) {
std::cout << x << std::endl;
}返回值:
- 返回引用允许函数直接返回变量本身,而不是拷贝。
1
2
3int& getValue(int& a) {
return a;
}别名:
- 为变量创建更简化的别名。
1
2int a = 42;
int& alias = a; // alias 是 a 的别名
(2) 适用指针
动态内存管理:
- 使用指针分配和释放动态内存。
1
2int* ptr = new int(10);
delete ptr; // 手动释放内存数组与迭代:
- 指针常用来访问数组元素。
1
2
3
4int arr[3] = {1, 2, 3};
for (int* ptr = arr; ptr < arr + 3; ++ptr) {
std::cout << *ptr << " ";
}数据结构:
- 指针是数据结构(如链表、树)的核心基础。
1
2
3
4struct Node {
int data;
Node* next;
};需要动态改变指向时:
- 指针可以灵活地改变指向,适合需要频繁切换指向的场景。
5. 总结对比
| 特征 | 引用 | 指针 |
|---|---|---|
| 需要初始化 | 必须初始化 | 可以先声明后初始化 |
| 是否可以为空 | 不可以为空 | 可以是空指针(nullptr) |
| 绑定是否可更改 | 绑定后不可更改 | 可通过重新赋值更改指向 |
| 语法复杂度 | 更简单 | 更复杂,需要使用 * 号 |
| 灵活性 | 受限 | 更灵活 |
| 常见场景 | 函数传参、别名 | 动态内存、复杂结构 |
引用更简单、更安全,适合大多数普通变量操作;指针更灵活,适合动态内存和复杂结构的场景。在实际开发中,应根据场景和需求选择适合的工具。