指针小结

目录

本篇文章记录一些指针学习的心得总结。

声明指针

//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的话就是列改变。
  • 二维指针的话要用**才可以得到储存的值

猜你喜欢

转载自blog.csdn.net/hgyan25/article/details/79518327