C++学习笔记——数据结构
堆和栈的区别:
栈和堆都是⽤于存储程序数据的内存区域。
① 栈是⼀种有限的内存区域,⽤于存储局部变量、函数调⽤信息等。堆是 ⼀种动态分配的内存区域,⽤于存储程序运⾏时动态分配的数据。
② 栈上的变量⽣命周期与其所在函数的执⾏周期相同,⽽堆上的变量⽣命周期由程序员显式控制,可以(使⽤ new 或 malloc)和释放(使⽤ delete 或 free )。
③ 栈上的内存分配和释放是⾃动的,速度较快。⽽堆上的内存分配和释放需要⼿动操作,速度相对较慢。
C++内存分区:
① 栈:存储函数的局部变量、函数参数和函数调⽤信息。函数的调⽤和返回通过栈来管理。
② 堆:存储动态分配的内存,由程序员⼿动分配和释放。使用new、delete或malloc、free来进行堆内存的分配和释放。
③ 全局/静态区: 存储全局变量和静态变量。⽣命周期是整个程序运⾏期间。在程序启动时分配,程序结束时释放。
④ 常量区(只读区):存储常量数据,如字符串常量。
⑤ 代码区:存储程序的代码。
野指针:指向已被释放的或⽆效的内存地址的指针。
使⽤野指针可能导致程序崩溃、数据损坏或其他不可预测的 ⾏为。通常由以下⼏种情况产⽣——
① 释放后没有置空指针
int* ptr = new int; delete ptr; // 此时 ptr 成为野指针,因为它仍然指向已经被释放的内存 ptr = nullptr; // 避免野指针,应该将指针置为 nullptr 或赋予新的有效地址② 返回局部变量的指针
int* createInt() { int x = 10; return &x; // x 是局部变量,函数结束后 x 被销毁,返回的指针成为野指针 } // 在使⽤返回值时可能引发未定义⾏为③ 函数参数指针被释放
void foo(int* ptr) { // 操作 ptr delete ptr; } int main() { int* ptr = new int; foo(ptr); // 在 foo 函数中 ptr 被释放,但在 main 函数中仍然可⽤,成为野指针 // 避免:在 foo 函数中不要释放调⽤⽅传递的指针 }如何避免野指针——
① 释放内存后指针及时置空
② 避免返回局部变量的指针
③ 注意函数参数的生命周期,避免在函数内释放调用方传递的指针,或者通过引用传递指针
④ 使用智能指针 std::unique_ptr
无穷大和无穷小:long long 数据类型
long long x=LONG_MAX; //无穷大 long long y=LONG_MIN; //无穷小反转函数:reverse
| 场景 | 代码示例 | 说明 |
|---|---|---|
| 反转整个静态数组 | std::reverse(arr, arr + size); | arr是数组名(首地址),arr + size是尾后指针 |
| 反转 vector | std::reverse(vec.begin(), vec.end()); | 使用begin()和end()迭代器 |
| 反转 string | std::reverse(str.begin(), str.end()); | 字符串同样使用迭代器 |
| 反转数组的前 k 个元素 | std::reverse(arr, arr + k); | 只反转索引[0, k-1]的元素 |
| 反转数组的区间 [l, r] | std::reverse(arr + l, arr + r + 1); | 注意:区间是左闭右开,所以末尾要+1 |
| 反转 vector 的区间 | std::reverse(vec.begin() + l, vec.begin() + r + 1); | 同样遵循左闭右开规则 |
| 反转 list(双向链表) | std::reverse(lst.begin(), lst.end()); | list支持双向迭代器 |
| 反转普通数组(指针方式) | std::reverse(std::begin(arr), std::end(arr)); | C++11 起推荐,更安全直观 |
取整函数:三种——ceil()、floor()、round()
| 函数 | 作用 | 示例(参数 3.2) | 示例(参数 3.7) | 示例(参数 -2.3) |
|---|---|---|---|---|
ceil() | 向上取整(往大取整) | 4 | 4 | -2 |
floor() | 向下取整(往小取整) | 3 | 3 | -3 |
round() | 四舍五入 | 3 | 4 | -2 |
自增运算符:i++和++i,它们在返回值和性能上有重要区别:
1.i++(后置自增)
先使用当前值,再自增;返回自增之前的值
2. ++i(前置自增)
先自增,再使用新值;返回自增之后的值
int i = 5; int a = i++; // a = 5, i = 6 int b = ++i; // i = 7, b = 7 // 前置自增 ++i 的模拟实现 int& operator++() { // 返回引用 *this += 1; // 先自增 return *this; // 返回自身 } // 后置自增 i++ 的模拟实现 int operator++(int) { // 返回值,int参数用于区分 int temp = *this; // 创建临时变量保存原值 *this += 1; // 自增 return temp; // 返回原值 }性能:
1. 单独使用时(如for循环的增量部分),两者效果相同
2. 涉及返回值调用时不一定相同(如上述代码所示)
3.++i通常比i++更高效,因为i++需要创建临时对象
static 静态声明:
静态局部变量在第一次函数被调用时创造并初始化,但在函数退出时它不死亡,而是保持其值等待函数下一次被调用。下次调用时不再重新创造和初始化该变量,而是直接用上一次留下的值为基础来进行操作。
void exampleFunction() { static int count = 0; // 静态局部变量 count++; cout << "Count: " << count << endl; } //每次调用函数时输出count值都加一,而不是总输出1