目录
本篇文章记录一些指针学习的心得总结。
声明指针
//typeName *pointerName
//for example:
int * pointer1;
double *pointer2;
指针赋值
- 将常量的内存地址赋值为指针,用取址符&来获取常量的地址
- 用new运算符返回未命名的内存的地址
//第一种方式:
int a = 100;//a为类型常量
int *pointer1 = &a;//将a的地址赋值为pointer1
//第二种方式
double *pointer2 = new double;
*pointer2 = 100.1;
delete pointer2;
第一种方式中,* pointer1 = 100; pointer1 = &a = 16进制表示的地址
sizeof(* pointer1) = 4 //其实求指针类型的大小,即求*pointer1的大小
sizeof(pointer1) = 8 //其实求指针的大小,即求pointer1的大小(64位系统)
第二种方式中,用了new来分配内存,new运算符会根据类型来确定需要多少字节的内存,然后找到这样的内存,并返回地址。接下来就将地址赋值为pointer2。pointer2是指向double的指针。最后一般要用delete运算符来释放内存,将是用完的内存归还给内存池。如果没有配对使用的话,会出现内存泄漏
。
Attention:
1. 内存泄漏就是被分配的内存再也无法使用了,如果内存泄漏严重的话,程序将由于不断寻找更多内存而终止。
2. new分配的内存块通常与常规变量声明分配的内存块不同。变量一般存储在栈中的内存区域中,new存储在堆或者自由存储区(free store)的内存区域中。
3. 不要删除同一个内存块两次
用new来创建动态数组
- 静态联编:在编译时给数组分配内存。编写时要指定数组的长度。
- 动态联编:在运行的阶段需要数组,就创建它,如果不需要就不创建。在程序运行时可以选择数组的长度。创建的数组也叫动态数组。编写时在运行时指定数组的长度。
//创建动态数组
//typeName * pointerNmae = new typeName [num_elements]
int size = 100;
int *pointer3 = new int[size];
pointer3[0] = 1;
pointer3[1] = 2;
delete []pointer3;
使用new,delete的规则:
- 不要用delete来释放不是new出来的内存
- 不要用delete释放同一个内存两次
- 使用new时,如果new不带方括号,delete也不带括号;如果new时带方括号,则使用delete时也要带方括号
- 对于空指针应用delete也是安全的。
给指针解除引用
解除引用意味着获得指针指向的值。
- 用运算符( * )
- 使用数组表示法, pointer[0] 等价于 * pointer
数组和指针其实是可以通用的
#include<iostream>
using namespace std;
int main(){
int array[6] = {0,1,2,3,4,5};
cout<<*(array+2)<<endl;//把数组名array看成是指针来计算
int *pointer = new int[5];
pointer[0] = 100;
pointer[1] = 1000;
cout<<pointer[0]<<" "<<*(pointer+1)<<endl;//pointer虽然是指针,也可以作为数组名来调用。
return 0;
}
//输出结果
2
100 1000
指针和指针指向的值
double *pointer2 = new double;//pointer2是指向double的指针
*pointer2 = 100.1;//*pointer2完全等价于一个double值
delete pointer2;
double a = 100.1;
double *pointer2;
pointer2 = &a;
指针和数组
int *dynamicArray = new int[10];
delete []dynamicArray;
int staticArray[10];
sizeof(dynamicArray) = 8,sizeof用于动态数组名时,返回的是指针长度
sizeof(staticArray) = 40,sizeof用于数组名是,返回的是整个数组的长度
int staticArray[10];
staticArray[1] = 100;
int *array1 = staticArray;//数组名staticArray 等价于 &staticArray[0]
cout<<*(array1+1)<<endl;//指针算术 相当于staticArray[1]=100
c++把数组名看做是数组的第一个元素。
指针算术
C++允许指针和整数加减。
加1的结果表示在原来的地址值加上指向的对象占用的总字节数。
两个指针之差得到一个整数。
#include<iostream>
using namespace std;
int main(){
int array[6] = {0,1,2,3,4,5};
int *start = array;
cout<<*start<<" at "<<start<<endl;
cout<<*(start+1)<<" at "<<start+1<<endl;
int *end = &array[5];
cout<<"end: "<<end<<" start: "<<start<<" end - start: "<<end - start<<endl;
return 0;
}
//输出结果
0 at 0x6ffe10
1 at 0x6ffe14
end: 0x6ffe24 start: 0x6ffe10 end - start: 5
指针和字符串
#include<iostream>
#include<cstring>
using namespace std;
int main(){
char str[10] = "rose";
cout<<strlen(str)<<endl;//长度为4
cout<<str<<" at "<<&(str)<<endl;//rose at 0x6ffe40
cout<<str+1<<endl;//ose,这里传入了str[1]的地址,所以输出它后面的所有字符串
cout<<&str<<" "<<&str+1<<endl;//0x6ffe40 0x6ffe4a
cout<<sizeof(str)<<endl;//10
return 0;
}
这里可以看到打印str时,提供了一个字符的地址,编译器cout会从该字符开始打印,知道遇到空字符为止。str只是一个char的地址,这个指针指向字符串的开头。可以通过取址符&来获取str的首地址。
按照上面的分析,下面的代码输出的结果就是rose。
cout<<&str[0]<<endl;//从该字符的位置开始打印,止到遇到空字符为止。所以整个字符串都输出来了。
但是呢,如果只需要输出第一个字符咋办呢。str[0]或者*str就表示第一个字符啦。
strcpy的使用
strcpy()将字符串从一个位置复制到另一个位置
const char *bird = "bear";//const表示可以访问,但不能修改
cout<<bird<<endl;//"bear"是字符串的首地址,在这里,输出bird其实就是输出bear
char str[10] = "rose";
char *p = new char[10];
strcpy(p, str);//这里只是将字符串复制到新的分配的空间里。所以两者的地址还是不一样的。
cout<<p<<" "<<str<<endl;//将str的值复制到p上。
cout<<&p<<" "<<&str<<endl;
//输出结果
rose rose
0x6ffe38 0x6ffe40
链表使用
用new、delete来创建链表的结点。
#include<iostream>
#include<cstring>
using namespace std;
struct node{
node *next;
int val;
};
int main(){
node *n1 = new node;
node *n2 = new node;
node *n3 = new node;
node *n4 = new node;
n1->val = 1;
n2->val = 11;
n3->val = 111;
n4->val = 1111;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
node *pHead = new node;
pHead->next = n1;
pHead = pHead->next;
while(pHead){
cout<<pHead->val<<endl;
pHead = pHead->next;
}
delete n1,n2,n3,n4,pHead;
return 0;
}
//输出结果
1
11
111
1111
C++管理数据内存的方式
- 自动存储: 常规变量,所属函数被调用时自耦定产生,在该函数结束时消亡。
- 静态存储:整个程序执行期间都存在的存储方式,在函数外定义它或者声明变量时用static
- 动态存储:new、delete,它们管理了一个内存池(自由存储空间或者堆),生命周期不受程序和函数的生存时间控制。可能导致占用的自由存储区不连续,使得跟踪新分配的内存的位置更困难。
二维指针
进阶版的指针,搞懂这个就”无敌”了(逃。
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int n[][3] = {10,20,30,40,50,60};
int (*p)[3];//指向三个元素为一个数组的指针
p = n;
cout<<*(*(p+1)+2)<<endl;//第二行第二列,等价于 *(p+1)[2]
cout<<p<<" "<<p+1<<" "<<sizeof(*p)<<endl;//&p[0][0]
cout<<*p<<endl;//p[0][0]
cout<<*((*p)+1)<<" at "<<(*p)+1<<endl;//列,p[0][1]
cout<<*((*p)+2)<<" at "<<(*p)+2<<endl;//列,p[0][1]
cout<<**p<<" at "<<*p<<endl;//p[0][0]
cout<<**(p+1)<<" at "<<*(p+1)<<endl;//行,p[1][0]
return 0;
}
(*p)[3]
,这是一个指向三个元素为一个数组的指针
*p
其实是&p[0][0]的地址,等价于&(* p[0])
p+1
其实就是第二个数组,也就是第二行的首个地址,等价于&(* p[1]),这里的+1
其实已经加了(3*sizeof(int))个字节。
(*p)+1
这里是第一行的第二个元素的地址,也就是p[0][1],这里的+1
是加上sizeof(int)个字节
*(p+1)
其实也是第二行的首个地址
*(p+1)+2
这里是结合了上面的分析,首先,行移动一行,列移动2列,得到的是p[1][2]的地址
总结一下啦,简单粗暴——
- 靠近p的话就是行改变,远离p的话就是列改变。
- 二维指针的话要用**才可以得到储存的值