指针变量里存放地址,地址里的内容是真正的数据
指针就是地址!
1. 指针概念
int i=30; char a='t';
int* pi;
指针也是变量
在32位系统中,指针的宽度是4字节宽(32bit)
pi = &i;
把i地址的编号赋值给pi
cout<<*pi;//地址的内容
cout<<pi;//地址,&i
如何看指针类型?
众多文章都列举了各种类型的指针,对于它们的解析也很简单。
看优先级!!
() > [] > *
例如:
int *p[3]; 先是数组,后*即数组元素是指针,再int 指针指向的类型是int
2. 二级指针
指向指针的指针。通过二级指针间接访问数据,改变一级指针的指向问题。
int i = 30;
int *pi = &i;
int **ppi = π
*pi=10;
改变一级指针的值
int j=40;
*pi=&j;
cout<<**ppi<<endl;
改变一级指针的指向
3. 指针与数组
- 指针数组 数组每个成员是指针
char * ch[10];
二级指针和指针数组等价
char **p;
char* array[N]; array = &array[0]; //array[0] 本身是 char*型;
char **p = array;
- 数组指针 指针指向一个数组
char (*ch)[10];
讨论:a 和&a 之间的赋值区别?
char a[5]={'A','B','C','D'};
char (*p3)[5] = &a;
char (*p4)[5] = a;
&a 是`整个数组的首地址
a 是数组首元素的首地址
其值相同但意义不同,p3 这个定义的“=”号两边的数据类型完全一致,而p4 这个定义的“=”号两边的数据类型就不一致了
所以p4会有警告!
指针变量和整数加减?
是指针变量的地址与指针类型大小*整数;
4. void* 指针
一种特殊的指针类型,可用于存放任意对象的地址。
- 如果要把 void 类型的指针赋值给其他类型的指针,需要进行显式转换
- void指针只有强制类型转换后才可以正常对其操作
因为对该地址中到底是个什么类型的对象并不了解,因此不能直接操作 void* 指针所指的对象。
只能以 void* 的视角来看内存空间也就是仅仅是内存空间,没办法访问内存空间中所存的对象,因此只有对其进行类型转换之后才可以对其进行相应的访问
这里在工作中经常用到,比如dll用到的void指针、向上类型转换用到的void指针等等。
extern "C" bool __stdcall CreateObject( void** pData, int id)
{
if (id == 1) *pData = new Idata1();
if (id == 2) *pData = new Idata2();
return false;
}
5. 函数与函数指针
当需要通过函数返回值来判断函数调用是否成功,又需要把数据传递出来,此时,我们就要用到多参返回,多参返回都是通过传递调用空间中的空间地址来实现的
- 函数的本质是一段可执行性代码段。函数名,则是指向这段代码段的首地址
void print()
{
printf("china\n");
}
int main()
{
void (*pf)() = print; //void (*pf)() = &print;
pf(); //(*pf)();
pf = dis;
pf();
return 0;
}
讨论:理解delete或free的作用,删除指针到底删了什么?
注意:new--------delete
malloc--------free
new的内存从自由存储区(也可以在堆上分配内存)
分配,而malloc的内存从堆分配
(关于内存布局见 c++内存理解)
int *p= new int ;
*p=3;
cout<< "将3赋给p的地址后,指针p读取的值:" <<*p<<endl;
delete p;
cout<< "删除空间后,指针p读取的值:" <<*p<<endl;
//delete掉指针,还可以获得原来指针输出值
//delete仅仅释放掉该指针指向的内存空间,不会删除指针本身!!
//只是清空该指针所指的堆中的对应空间,但该指针变量在栈中的值并没有清空它还是指向原来分配的内存空间(但是该内存空间已经不属于该指针了,CPU随时可把该指针原来所指的空间分配给任何一个指针变量)
long *p1= new long ;
*p1=100;
cout<< "创建新空间后,指针p中保存的地址:" <<p<<endl;
cout<< "指向新空间的指针p1保存的地址:" <<p1<<endl;
//指针p和p1指向同一个内存空间;
//原因:编译器的作用,默认将释放掉的内存空间回收然后分配给新开辟的空间
//所以是新开辟的保存long类型变量的空间
//这里有安全问题!!
//若此时对*p重新赋值,连带*p1也会改动。下面
//*p就属于野指针!!
//不指向任何内存空间的指针我们称之为野指针,野指针所指向的地方是随机的!!
*p=23;
cout<< "将23赋给p的地址后,指针p读取的值:" <<*p<<endl;//23
cout<< "将23赋给p的地址后,指针p1读取的值:" <<*p1<<endl;//23而不是100
delete p1;
解决方案:
删除完指针要将其设置为空指针(p = NULL)
stdio.h中关于关键字NULL的定义为 0
一般,NULL 也用来判断指针是否初始化成功,可以用此加以判断。
讨论:多次free或delete指针,为什么会报异常?
第一次,清空指针指向的内存空间;
第二次,指针没有指向的内存空间,编译器提示错误
讨论:经常说的 ”内存泄漏“ 到底是什么?什么原因会导致呢?
内存泄漏:用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,即内存空间使用完却未回收。
内存泄漏比较难发现…
原因1:一般用完内存或调用malloc失败才会导致问题;
原因2:delete掉一个void*类型指针,导致未调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄露;
探讨:delete掉一个void*类型指针,不调用析构函数
:一般,一个类的指针,当使用delete函数来删除该指针时,会调用该指针当前类型的析构函数
classB* pClass = (classB*)new classA;//指针类型:classA
delete pClass;//delete时,调用的是classB的析构函数
但void* pClass本身没有析构函数,当类指针强转为void *,则delete时不调用析构函数!~
原因3:new创建一组对象数组,内存回收的时候却只调用了delete而非delete []来处理,导致只有对象数组的第一个对象的析构函数得到执行并回收了内存占用,数组的其他对象所占内存得不到回收,导致内存泄露;
class Object1
{
int a;
int b;
};
int main() {
Object1* arry1 = new Object1[100];//创建对象数组arry1并返回数组首地址;
Object1* arry2 = new Object1[100];//创建对象数组arry2并返回数组首地址;
delete []arry1;//回收了数组arry1里的所有对象动态创建时占用的内存空间;
delete arry2;//回收了数组arry2里的第一个对象动态创建时占用的内存空间,导致其他对象的内存空间泄露;
注意:c++没有垃圾回收机制,需要正确释放内存。
c++发明人也说过是有意设计c++不依赖于自动垃圾回收机制(java是拥有这一机制的),目的类似于不想为了不必要的功能付出代价,原因:指针没有共同的基类,因为允许类型转换,无法知道指针它真正的类型,而java是有共同基类的
指针也带来了很多问题,主要是:
1.当指向内存初始化失败
2.内存越界
3.未正常释放
第六点基本可以解决
6. 智能指针
1.定义:行为上是指针,获取资源即初始化,释放资源即析构。可以自动对指针进行释放。
2.背景:由于new动态分配内存时,有时会忘记内存释放
由于对于智能指针不熟悉,基本不敢用,但自动释放这个功能真的强,这里详细记录下怎么用吧。
智能指针实际是个栈对象。
3.使用
智能指针是c++11提供的,头文件
Derived* a = new Derived();
a->myfunc1();
shared_ptr<Derived> ptr(new Derived());
shared_ptr<Derived> ptr1 = make_shared<Derived>();//使用make_shared初始化,可以提高性能,具体为啥不理解..(待研究)
ptr->myfunc1();
ptr1->myfunc1();
类型 | 解释 |
---|---|
shared_ptr | |
unique_ptr | |
weak_ptr |
7. 使用指针的注意点
1.使用指针,一定要先判空!
2.通过new的对象指针,要delete + 指向空!(是分配到堆区需要手动释放)