指针
为对象起了另外一个名字(引用即别名)
int int_value = 1024;
int& refValue = int_value; // refValue指向int_value,是int_value的一个别名
int& refValue2; // 错误:引用必须初始化
-
注意:
-
引用并非对象,只是为一个已经存在的对象起了一个别名
-
引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
-
int &ref_value = 10; // 错误
-
-
引用必须初始化,所以使用引用之前不需要测试其有效性,因此使用引用可能会比使用指针效率高。
-
-
指向常量的引用是非法的
double& ref = 100; // 错误
const double& ref = 100; // 表示一个常量的引用,所以我们也可以反推,没有const为变量,变量怎么能赋值呢。
指针和引用
-
引用对指针进行了简单封装,底层仍然是指针
-
获取引用地址时,编译器会进行内部转换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gEOgA3y7-1620553505413)(C:\Users\君莫笑\AppData\Roaming\Typora\typora-user-images\image-20210509153342118.png)]
指针和数组
- 存储在一块连续的内存空间中
- 数组名就是这块连续内存空间的首地址
int main(){
double socre [] {11,22,33,44,55}
double * prt_score = score; // 指向数组
cout << prt_score[3] << endl; // 输出了 44
cout << sizeof(score) << '\t' << sizeof(prt_score) << endl;
// 计算数据类型或对象的长度大小
// 输出了 40 4
// score数组为double类型数组,占8个字节,5个数组即40个字节大小
// 指针是一个地址,他的大小一般为4个字节
}
指针运算
- 指针的递增和递减(++、–)
int i;
double score[5]{98,87,65,43,76};
double * prt_score;
ptr_score = score;
for (i=0;i<5;i++){
cout << *ptr_score << endl;
}
第一次取的地址为:
当++ 一次后,指针平移到下一个单位:
- 注意:
- 一个类型为T的指针的移动,以szieof(T)为移动单位
- 当前的数组类型为double,所以移动一次8个字节
- 一个类型为T的指针的移动,以szieof(T)为移动单位
数组与指针小结
-
数组名就是这块连续内存单元的首地址
-
int num[50]; // num 是数组名,也可以理解成数组的首地址
-
num的值与&num[0] 的值是相同的
-
数组到底 i+1 个元素可表示为:
- 第 i + 1个元素的地址 : &num[i+1] 或 num + i + 1
- 第 i + 1 个元素的值: num[i+1] 或 *(num+i+1)
-
为指向数组的指针赋值
-
int * ptr_num = num; 或 int * prt_num = &num[0];
-
-
指针变量可以指向数组元素
-
int * ptr_num = &num[4]; 或 int * ptr_num = num + 4;
-
-
内存中的栈区和堆区
其实前面有学,但是我基本忘光了。
下面的内存分类我从 蝶开三月 那里copy过来的。
**而C语言的内存模型分为5个区:栈区、堆区、静态区、常量区、代码区。**每个区存储的内容如下:
1、栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了
其操作方式类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,效率很高,但是分配的内存量有限,比如iOS中栈区的大小是2M。
2、堆区:就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。
3、静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。
4、常量区:常量存储在这里,不允许修改。
5、代码区:顾名思义,存放代码。
动态分配内存
-
使用new分配内存
- 指针真正的用武之地:在运行阶段分配未命名的内存以存储值
- 在此情况下,只能通过指针来访问内存!
-
用delete释放内存
-
与new配对使用
-
不要释放已经释放的内存
-
注意
-
// 1.不要创建两个指向同一内存块的指针,有可能误删除两次 int * ptr = new int; int * ptr1 = ptr; delete ptr; delete ptr1;
-
-
-
不能释放声明变量分配的内存
-
// 1. 在运行阶段为一个int值分配未命名的内存
// 2. 在使用指针来访问(指向)这个值(右 - > 左)
int * ptr_int = new int;
delete prt_int; // 释放由new 分配的内存
// 创建和释放总是成对存在的
p - 栈区 // 在堆区分配了一块int 类型空间
int * p = new int; // 在这你让整型 p 指针 指向了在堆区新创建的int空间
p++; // 你在这里的++ 将指针位置移动了,而刚刚的分配的内存还在
// 没有指针指向它了(没用到),你也没有删除,造成了内存泄露
-
内存决策:
-
编译时(包办婚姻)
-
int num[56];
-
-
运行时
-
int* nums = new int[5];
-
-
cout << sizeof(num) << '\t' << sizeof(nums) << endl; // 输出的是 20 4 // szieo(num)是20(5*4) sizeof(nums) 是 4 (1*4) // int的位数是32位,也就是4字节 // num[5]的内存是在栈内存里的, // new int[5] 是在堆内存里的,而我们创建的指针也是存在栈内存中的 // 而sizeof是取栈内存大小的
-
-
使用new创建动态分配的数组
-
int * intArray = new int[10];
- new运算符返回第一个元素的地址
-
使用delete[] 释放内存
- delete [] intArray;
- [] 释放整个数组
- delete [] intArray;
-
int * ptr_int = new int; short * ptr_short = new short[500]; delete ptr_int; delete [] prt_short;
-
-
关于new和delete使用的规则:
- 1.不要使用delete释放不是new分配的内存
- 有时你释放的内存,由于没有其他东西来覆盖,那个值依然存放在那个内存。
- 2.不要用delete 释放同一内存两次
- 3.如果使用new[]为数组分配内存,则对应delete[]释放内存
- 4.对空指针使用delete是安全的
- 1.不要使用delete释放不是new分配的内存
补充:程序的内存分配
- 栈区(stack)
- 由编译器自动分配释放,一般存放函数的参数值,局部变量的值等
- 操作方式类似数据结构中的栈–先进后出
- 堆区(heap)
- 一般由程序员分配释放,若程序不释放,程序结束时可能有操作系统回收
- 注意:与数据结构的堆是两回事,分配方式类似链表
- 全局区(静态区-static)
- 全局变量和静态变量是储存在一起的
- 程序结束后由系统释放
- 文字常量区
- 常量字符串就放在这里,程序结束由系统释放
- 程序代码区
- 存放函数体的二进制代码