c++基础重补-Day02
指针vs引用
基础:
1.存储方式:
1.1指针:是变量,存储的是内存地址。
1.2引用:是变量的别名,不存储地址,通过别名直接访问原始变量。
2.初始化:
2.1指针:可以不初始化(但应避免,以免成为野指针),初始化时需要显式地给出地址。
2.2引用:必须在定义时初始化,且一旦初始化后不可再改变为引用另一个变量。
3.空值:
3.1指针:可以指向空(nullptr或NULL),表示不指向任何对象。
3.2引用:必须总是指向某个对象,不存在空引用的概念。
4.多级引用/指针:
4.1指针:可以有多级指针(如int**),指向另一个指针。
4.2引用:不支持多级引用,即不能定义引用的引用。
5.函数参数与返回值:
5.1指针:作为参数传递时,可以修改原始数据(如果指向了非const对象)。作为返回值时,需要注意生命周期问题。
5.2引用:作为参数传递时,同样可以修改原始数据(如果非const)。作为返回值时,更安全,因为不需要担心指针的生命周期问题。(这里的生命周期是指引用作为参数传递,作为作为返回值返回时,生命周期安全问题)
使用场景:
1.当需要指向空值或重新指向另一个对象时,使用指针。
2.当需要在函数间安全地传递大型对象或需要修改函数参数时,优先考虑引用(除非有指向空或重新指向的需求)。
3.在需要动态内存分配或管理内存时,使用指针。
4.在需要避免拷贝大型对象以提高效率时,使用引用(特别是作为函数参数或返回值)。
内存
new/delete/free
new 在堆上开辟连续的内存空间
1.
delete p(匹配new T)
先调用
p指向对象的析构函数,释放对象内部资源;回收堆内存还给操作系统。
2.
delete[] arr(匹配new T[5])
根据 new 时隐藏记录的元素个数,循环调用每个元素的析构函数;
整块连续数组内存回收。
致命匹配规则(必考)
new T→ 只能delete,不能delete[]
new T[]→ 只能delete[],不能delete混用产生未定义行为(内存泄漏 / 程序崩溃)
1. C 语言有没有 delete?
没有。
new / delete是 C++ 独有的关键字,C 语言语法完全不认识,编译直接报错。2. C 标准配对:
malloc/calloc/realloc↔freeC 只有堆内存原始分配函数,只开辟裸内存,不涉及对象构造析构。
3. C++ 是不是不能用 malloc?
不是禁止,只是不推荐混用,分场景:
自定义类、带构造 / 析构的对象:必须用
new/delete;纯内置类型(int、char 数组):语法上 malloc+free 能跑,但 C++ 工程规范优先 new/delete。
4. C++ 标准配对:
new↔delete;new[]↔delete[]严格配对,不能交叉。
malloc/calloc/realloc
#include <stdio.h>
#include <stdlib.h>
void func(int *arr,size_t n){
for (size_t i=0; i < n; i++){
printf("%d - ",arr[i]);
}
}
int main(int argc, char const *argv[])
{
int *mal=(int* )malloc(5 * sizeof(int));
func(mal,5);
printf("\n");
int *cal=(int*)calloc(5, sizeof(int));
func(cal,5);
printf("\n");
int *rel=(int*)realloc(mal,3);
func(mal,5);
return 0;
}
malloc 开辟空间,内部都是杂乱的数据
calloc 开辟空间,内部全部initial为0realloc缩容或者扩容
内存分区
内存四区简单区分(程序运行时)
- 栈 stack 函数局部变量、函数形参、函数返回地址;自动分配自动释放;容量很小。
- 堆 heap malloc /calloc/realloc /new 手动申请,free /delete 手动释放;空间大。
- BSS全局 / 静态数据段 全局变量、static 静态局部变量、字符串常量。
- CODE代码段 函数二进制指令、const 只读常量。
必须用堆:
场景 1:数组太大,栈装不下(栈空间极小)
场景 2:数组长度运行时才确定(动态长度)
场景 3:函数结束后,数组数据还要给外部使用(最关键区别)
场景 4:数据需要长期保存,不随函数结束销毁(Cache)
sizeof 求数组长度 + malloc 动态数组长度与越界
一、sizeof 计算数组长度(仅栈固定数组可用)
1. 正确公式(局部完整栈数组)
c
运行
int arr[6] = {1,2,3,4,5,6}; // 总字节 ÷ 单个元素字节 = 元素个数 size_t len = sizeof(arr) / sizeof(arr[0]);原理:栈上完整数组,
sizeof(数组名)算出整块数组总占用字节。2. 两种场景 sizeof 完全失效
1)数组作为函数形参 函数内
int arr[]等价int* arr,sizeof(arr)只得到指针大小(64 位 8 字节),无法求长度。 解决:把数组长度单独作为参数传入函数。2)malloc 堆动态数组
int* p = malloc(n * sizeof(int));p 是指针,sizeof(p)永远是指针宽度,和分配多少元素无关,不能算长度。二、malloc 堆数组如何保存、判断长度
1. 核心结论
堆内存不会自动记录分配的元素数量,没有内置长度标记,只能手动用变量记录容量。 示例:
c
运行
int cap = 5; // 记录堆数组总容量 int* arr = (int*)malloc(cap * sizeof(int)); // 遍历、边界判断全部依靠 cap 变量 for(int i = 0; i < cap; i++){ arr[i] = i; } free(arr);2. realloc 动态扩容配套处理
扩容时同步更新容量变量 cap:
c
运行
cap *= 2; int* temp = realloc(arr, cap * sizeof(int)); arr = temp;三、数组下标越界问题(栈数组 / 堆数组统一规则)
无论栈数组还是 malloc 堆数组,访问超过分配范围的下标都属于未定义行为 UB,标准不规定固定后果。
栈数组越界后果:篡改其他局部变量、覆盖函数返回地址,程序乱码、崩溃、栈溢出攻击风险。
堆数组越界后果:破坏堆内存管理头部信息,后续调用 free () 大概率直接程序崩溃,内存结构损坏。
四、避坑总结背诵
只有函数内部固定长度栈数组,才能用
sizeof(arr)/sizeof(arr[0])求长度;函数形参数组、malloc 堆指针,sizeof 失效,堆必须手动变量存容量;
栈 / 堆数组都不能下标越界读写,均是未定义行为,会造成程序异常崩溃。
更多推荐


所有评论(0)