- C++笔记(三)【指针】
- 一、基础知识
- 1.1 定义
- 1.2 关于定义时int* p 和 int *p的写法
- 1.3 使用方式
- 1.4 特殊注意点
- 二、 指针类型
- 2.1 空指针
- 2.2 void* 指针
- 三、引用
- 3.1 什么是引用
- 3.2 引用与指针的关系
- 四、指针与数组
- 4.1 联系
- 4.2 区别
- 五、指针运算
- 5.1 指针的递增递减
- 5.2 指针的直接加减
- 5.3 取数组的元素
- 六、动态分配内存
- 七、关于程序的内存分配
- 7.1 类型
- 7.2 示例
- 八、指针与二维数组
指针:一个值为内存地址的变量(或数据对象)
内存中:
int 变量a(内存地址+内容)
int 指针变量b(内存地址+内容(变量a的地址))*
叫做b指向a
1.2 关于定义时int* p 和 int *p的写法1.int* p的写法偏向于地址,即p就是一个地址变量
2.int *p的写法偏向于值,*p是一个整型变量
3.声明中的*号和使用在的*号含义完全不同
推荐:采用第一种方法用int*来声明一个整型指针变量
1.3 使用方式int num=1000; int* ptr_num=# //但不能直接给一个常量地址 *ptr_num=90; //num=901.4 特殊注意点
#include二、 指针类型 2.1 空指针using namespace std; int main() { char ch='a'; char* ptr_ch=&ch; cout << ptr_ch << "t" << *ptr_ch << endl; //a a 会与想象的不同 //原因 char* str="唐柳健is"; //在c语言时代char*就默认后面跟的是字符串的地址,而不是字符的地址,因此上面打印的时候按照字符串的地址方式打印,肯定会出错 //解决方法强转成char型指针 cout << (char*)ptr_ch << endl; //此方法有时候编译器不让转 //改成这个方法 cout << (void*)ptr_ch << endl; return 0; }
一定要给指针初始值,要不然采用默认值很危险,为此可以用空指针,强烈建议初始化所有指针
int* kjk;//野指针 //下面三个等价 int* mine=nullptr; int* mine=0; int* mine=NULL; //要加上#include2.2 void* 指针
可以存放任意对象的地址
int nm = 10; int* ptr_n = # void* ptr_m = # // ptr_m==ptr_n
注意点
1.无法修改指向的变量值,因为类型不清楚
如:*ptr_m=90;
2.void指针类型一般用来:
拿来和别的指针比较(当不知道函数输出的指针属于什么类型的时候)
或者作为函数的输入输出
数组中也可以来存指针来节省空间大小
三、引用 3.1 什么是引用核心:为了使得代码写的更漂亮方便
定义:为对象起了别名
int int_value=2022; int& reValue=int_value; //错误,引用必须被初始化 int& reVla2;
注意:
- 引用并非对象,只是给一个已经存在的对象起了别名
- 引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起,如int& ref_value=10;
- 引用必须初始化,使用引用之前不需要测试其有效性,所以一定程度上比指针高效
指向常量的引用是非法的
可以改成
const double& ref=100;3.2 引用与指针的关系
- 引用对指针进行了简单的封装,底层仍然为指针
- 获取引用地址时编译器会进行内部转换
如下,相当于把星号指针封装了:
int num=100; int& rel_num=num; rel_num=90; cout << &num << "t" << &rel_num << endl; //实际上 int num=100; int* rel_num=# *rel_num=90; cout << &num << "t" << rel_num << endl;四、指针与数组 4.1 联系
数组:
存储在一块连续的内存空间中
数组名就是这块连续内存空间的首地址
#include4.2 区别using namespace std; int main() { double scores[]={2,5,65,7,2}; double* ptr=&scores[0]; cout << scores << "t" << ptr << endl; //scores=ptr //指向数组的指针可以进行下标操作 double* pt=scores; cout << pt[3] << endl; return 0; }
但是指针与数组不是一码事!
#include五、指针运算using namespace std; int main() { double scores[]={2,5,65,7,2}; double* pt=scores; cout << sizeof(scores) << "t" << sizeof(pt) << endl; //40 8 40显然是8字节*5=40 相当于打回原形了虽然数组名指向的是首地址 return 0; }
理解:指针的平移
5.1 指针的递增递减#include5.2 指针的直接加减using namespace std; int main() { double scores[]={2,5,65,7,2}; double* pt=scores; for(int i=0;i<5;i++){ cout << *pt++ << endl; //平移的是一个double类型大小的字节,这里为8,即使sizeof(double) } return 0; }
但是注意越界问题很危险
#include5.3 取数组的元素using namespace std; int main() { double scores[]={2,5,65,7,2}; double* pt=scores; pt+=2; cout << *pt << endl; pt-=2; cout << *pt << endl; cout << *(pt+3) << endl; cout << *(pt+i) << endl; return 0; }
第i个元素的地址:&num[i] 或 num+i
第i个元素的值:num[i] 或 *(num+i)
int* ptr=&num[4]; int* ptr=num+4; //等价的
很重要的一点!!
数组名不能加加,只有自由的指针才可以
不管是指针,尽量不要贸然的移动指针,尽量少在自身上++,可以+i
double scores[]={2,5,65,7,2};
double* pt=scores;
cout << *++scores << endl; //会报错的,因为++就相当于scores=scores+1 改变了,*(scores+2)则没有改变scores本身所以是可以的
六、动态分配内存
1.使用new分配内存
- 指针真正用武之地:在运行阶段分配未命名的内存以存储值
- 在此情况下只能通过指针来访问内存
#includeusing namespace std; int main() { //运行阶段为一个int值分配未命名的内存,使用指针来访问这个值 // ptr在栈区, 在堆区分配了一块int型空间 int* ptr=new int; *ptr=90; ptr++;//这个行为很危险,因为一旦++后上面的内存空间就变成内存泄漏,尽量避免这种操作 delete ptr; //一定要和new成对出现 //数组也可以这样 int* nums = new int[7]; int numss[7]; delete []nums; //sizeof(nums) 得到结果4字节,运行时候才会动态给空间 //sizeof(numss) 得到28 return 0; }
2.使用delete释放内存
不能释放声明变量分配的内存
另外,不要创建两个指向同一内存的指针,有可能误删两次
七、关于程序的内存分配 7.1 类型1.栈区
由编译器自动分配释放,一般存放函数的参数值,局部变量的值等,操作方式类似数据结构中的栈,先进后出
2.堆区(heap)
一般由程序猿分配释放,若没释放,程序结束时可能由操作系统回收,注意与数据结构中的堆概念不同,分配方式类似链表
3.全局区(静态区-static)
全局变量和静态变量存储在一起,程序结束后由系统释放
4.文字常量区
常量字符串就放在这里,程序结束时由系统释放
5.程序代码区
存放函数体的二进制代码
7.2 示例#include八、指针与二维数组using namespace std; int num1=0; //全局初始化区 int* ptr1; //全局未初始化区 int main() { //栈区 int num2; char str[] = "tlj is coder"; char* ptr2; //赋值的右边的字符串及数字都是在常量区的,左边在栈区 char* ptr3="tlj"; double nums[23]={2,3,4,5}; //全局(静态)初始化区 static int num3=1024; //右边,即分配的内存在堆区,左边的指针本身在栈区 ptr1 = new int[10]; ptr2 = new char[30]; return 0; }
直接看代码理解
#includeusing namespace std; int main() { //二维数组的一维作为了行,这个指针相当于五个一维数组组成的 int (*p2)[3]= new int[5][3]; p2[2][3]=90; for(int i=0;i<5;i++){ for(int j=0;j<3;j++){ cout << p2[i][j] << 't'; cout << *(*(p2+i)+j) << ','; } cout << endl; } cout << "另一种" << endl; int arrays[5][3] = {{1,2,3},{3,4,5},{7,8,9},{1,5,6},{4,7,8}}; int (*p1)[3] = arrays; //第二行第一列的地址,可以与下面结合观察,他们每个行的地址是相邻的 cout << &p1[1][0] << endl; for(int i=0;i<5;i++){ for(int j=0;j<3;j++){ //指的是每行数组的第一个元素的地址 cout << p1+i << 't'; } cout << endl; } return 0; }
指针还能用于,如结构体中不能有函数,则可以用指针指向一个函数。这样结构体里就有方法了,这也是实现面向对象的一个方式。



