IE盒子

搜索
查看: 85|回复: 1

C++指针总结,看这一篇就够了

[复制链接]

4

主题

7

帖子

15

积分

新手上路

Rank: 1

积分
15
发表于 2022-12-2 12:43:33 | 显示全部楼层 |阅读模式
3 指针

指向地址的数据类型。
3.1指针简介


  • 1.如何获取地址? &---地址运算符
  • 2.指针类型?简单数据类型、复合类型、类相对应的指针类型
  • 3.指针存储空间?各指针的存储空间都相同,因为他们都指向地址,而一个系统中地址的范围是确定的
  • 4.指针赋值?获取变量的地址赋值给对应变量的指针即可
  • 5.如何获取指针指向地址存储的值?---*运算符
  • 6.注意事项:避免产生野指针
  • (1)每个指针变量名,都需要使用一个。
  • (2)一定要在对指针应用解除引用(*)运算符之前,将指针初始化为一个确定的、适当的地址。
  • (3)不能直接将整数赋值给指针,但是可以使用强制类型转换将整数转换为地址然后赋值给指针。强转的结果也可能引发异常。
3.2 指针与动态内存分配

3.2.1 new


  • (1)new创建动态变量:typename * pointer_name = new typename;
  • (2)new创建动态数组:typename * pointer_name = new typename[num_elements];
  • (3)内存分配失败:如果没有足够的存储空间,则内存分配失败,会引发异常
  • (4)存储地点:变量的值存储在栈(stack)内存区域中,new的内存存储在堆(heap)或自由存储区(free store)中
3.2.2 delete

释放内存,由new分配的内存,需要程序员手动释放,如果未释放则会导致内存泄漏(memory leak)。如果内存泄漏严重,则程序将由于不能寻找更多内存而终止。
注意事项:

  • (1)不要使用delete释放不是new分配的内存块
  • (2)不要使用delete释放同一个内存块两次
  • (3)使用new创建的动态变量,对应使用delete(不带方括号)释放内存
  • (4)使用new创建的动态数组,对应使用delete []释放内存 要求(3)和(4)一定要一一对应
  • (5)对空指针应用delete是安全的
3.3 指针算数

将指针变量加1(or 减1)后,增加(or 减少)的量等于它指向的类型的字节数。
3.4 指针与数组

指针与数组名最大的区别就是,可以修改指针的值,但是不能修改数组名的值。 获取数组元素的值可以使用数组名也可以使用指针
arrayname becomes *(arrayname + i)
pointername becomes * (pointername+i)
sizeof(arrayname)---返回值为整个数组的长度,arrayname不能 += 1
sizeof(pointername)---返回值为指针所占内存空间大小,能+=1
数组名表示第一个元素的地址 对数组名取&地址表示的是整个数组的地址,它是一种特殊的指针:
int (*pas)[20]---一个例子,其他数据类型也可;
sizeof(pas) == 4,如果对pas+1的话,增加的是整个数组的长度
对数组名+1的话,返回值增加了数组中单个元素的长度.
3.5 指针与字符串

在cout和多数C++表达式中,char数组名、char指针以及用括号括起的字符串常量(const char )都被解释为字符串第一个字符的地址。 用括号括起的字符串常量被存储在某一位置,如果将其赋值给指针,则该指针指向该字符串常量,该指针可以获取字符串但是不可以更改字符串 不应该对没有new分配内存的char指针执行cin操作,也不应该尝试获取野指针所指向的内容。 如果给cout提供一个char ,则cout默认打印字符串;如果需要显示改字符串地址,则应该强制类型转换char * 为int 在为char 指针使用new分配内存时,可以根据字符串的长度为其分配内存,但是记得要给'\0'留空间噢。
3.6 指针new动态结构体

创建结构体:见复合类型结构体。 new结构体格式:inflatable * ps = new inflatable;//假设inflatable是我们创建的结构体 使用指针访问new结构体的元素:箭头成员运算符(->),或者(*指针).成员。
3.7 指针数组、数组指针、指针的指针

指针数组:由指针组成的数组 数组指针:指向数组的指针
3.8 举例

代码:
/*
Project name :          _9pointer
Last modified Date:     2022年3月14日21点22分
Last Version:           V1.0
Descriptions:           指针类型
*/
#include<iostream>

int main()
{
    using namespace std;
    /*
    指针:指向地址的数据类型。
        1.如何获取地址? &---地址运算符
        2.指针类型?简单数据类型、复合类型、类相对应的指针类型
        3.指针存储空间?各指针的存储空间都相同,因为他们都指向地址,而一个系统中地址的范围是确定的
        4.指针赋值?获取变量的地址赋值给对应变量的指针即可
        5.如何获取指针指向地址存储的值?---*运算符
        6.注意事项:避免产生野指针
            (1)每个指针变量名,都需要使用一个*。
            (2)一定要在对指针应用解除引用(*)运算符之前,将指针初始化为一个确定的、适当的地址。
            (3)不能直接将整数赋值给指针,但是可以使用强制类型转换将整数转换为地址然后赋值给指针。强转的结果也可能引发异常。
    */
    cout << "指针****************************************************************" << endl;
    int* p_int;
    char* p_char;
    cout << "sizeof(p_int) = " << sizeof(p_int) << endl;//sizeof(p_int) = 4
    cout << "sizeof(p_char) = " << sizeof(p_char) << endl;//sizeof(p_char) = 4
    int love = 999999;
    cout << "love = " << love << endl;//love = 999999
    p_int = &love;//指针赋值
    cout << "p_int = " << p_int << endl;//p_int = 00EFFC60
    cout << "*p_int = " << *p_int << endl;//*p_int = 999999
    //注意事项1
    int* x, y;//此处声明的是int指针类型的x和int类型的y
    //注意事项2
    //*x = 1000;//这句执行之后指针会指向一个不知道在哪的地址上,可能会导致一些bug
    //注意事项3
    //x = 0x99999999;//不允许的
    //x = (int*)0x99999999;//允许,但是在使用解除引用*运算符后可能引发异常,因为你也不知道它真正指向了哪里
    //cout << "x = " << x << endl;
    //cout << "*x = " << *x << endl;
    //new
    /*
        7.new:
            (1)new创建动态变量:typename * pointer_name = new typename;
            (2)new创建动态数组:typename * pointer_name = new typename[num_elements];
            (3)内存分配失败:如果没有足够的存储空间,则内存分配失败,会引发异常
            (4)存储地点:变量的值存储在栈(stack)内存区域中,new的内存存储在堆(heap)或自由存储区(free store)中
        8.delete:释放内存,由new分配的内存,需要程序员手动释放,如果未释放则会导致内存泄漏(memory leak)。如果内存泄漏严重,则程序将由于不能寻找更多内存而终止。
            注意事项:
                (1)不要使用delete释放不是new分配的内存块
                (2)不要使用delete释放同一个内存块两次
                (3)使用new创建的动态变量,对应使用delete(不带方括号)释放内存
                (4)使用new创建的动态数组,对应使用delete []释放内存
                    要求(3)和(4)一定要一一对应
                (5)对空指针应用delete是安全的
        9.指针算数
            将指针变量加1(or 减1)后,增加(or 减少)的量等于它指向的类型的字节数。
    */
    cout << "new*******************************************************************" << endl;
    int* p_pointer = new int;
    int* p_test = p_pointer;
    delete p_pointer;//释放分配的内存
    //delete p_test;//已经释放的内存块不能再释放,会触发断点。。
    p_test = new int[3]{ 1,2,3 };
    p_test[1] = 999;//更改动态数组的元素
    cout << "p_test[1] = " << p_test[1] << endl;//p_test[1] = 999 //获取动态数组的元素
    cout << "p_test = " << p_test << endl;//p_test = 00FF0480
    p_test = p_test + 1;
    cout << "p_test = " << p_test << endl;//p_test = 00FF0484
    cout << "p_test[1] = " << p_test[1] << endl;//p_test[1] = 3
    p_test = p_test - 1;
    cout << "p_test = " << p_test << endl;//p_test = 00FF0480
    cout << "p_test[1] = " << p_test[1] << endl;//p_test[1] = 999
    delete[] p_test;//释放动态数组
    double* p_double = new double[3]{ 9.9,9.99,9.88 };
    cout << "p_double = " << p_double << endl;//p_test = 00FF0484
    p_double = p_double + 1;
    cout << "p_double = " << p_double << endl;//p_test = 00FF0484
    p_double = p_double - 1;
    cout << "p_double = " << p_double << endl;//p_test = 00FF0484
    delete[] p_double;
    /*
        10.指针与数组
            指针与数组名最大的区别就是,可以修改指针的值,但是不能修改数组名的值。
            获取数组元素的值可以使用数组名也可以使用指针
            arrayname becomes *(arrayname + i)
            pointername becomes * (pointername+i)
            sizeof(arrayname)---返回值为整个数组的长度,arrayname不能 += 1
            sizeof(pointername)---返回值为指针所占内存空间大小,能+=1

            数组名表示第一个元素的地址
            对数组名取&地址表示的是整个数组的地址,它是一种特殊的指针:int (*pas)[20]---一个例子,其他数据类型也可;
                sizeof(pas) == 4,如果对pas+1的话,增加的是整个数组的长度
            对数组名+1的话,返回值增加了数组中单个元素的长度
    */
    cout << "指针与数组*************************************************************" << endl;
    int arrayname[4] = { 1,2,3,4 };
    int* pointername = arrayname;//pointername指向数组第一个元素的地址
    cout << "pointername = " << pointername << endl;//pointername = 008FFD74
    cout << "*(arrayname + 1) = " << *(arrayname + 1) << endl;//*(arrayname + 1) = 2
    pointername = &arrayname[1];//pointername指向数组第二个元素的地址
    cout << "pointername = " << pointername << endl;//pointername = 008FFD78
    pointername += 1;//allowed
    cout << "*(pointername + 1) = " << *(pointername + 1) << endl;//*(arrayname + 1) = 3
    //arrayname += 1;//not allowed
    int(*pas)[4] = &arrayname;
    cout << "(*pas)[2] = " << (*pas)[2] << endl;//(*pas)[2] = 3
    cout << "sizeof(arrayname) = " << sizeof(arrayname) << endl;//sizeof(arrayname) = 16
    cout << "sizeof(pointername) = " << sizeof(pointername) << endl;//sizeof(pointername) = 4
    cout << "sizeof(pas) = " << sizeof(pas) << endl;//sizeof(pas) = 4
    cout << "&arrayname = " << &arrayname << endl;//&arrayname = 008FFD74
    cout << "&arrayname + 1 = " << &arrayname + 1 << endl;//&arrayname + 1 = 008FFD84
    cout << "arrayname = " << arrayname << endl;//arrayname = 008FFD74
    cout << "arrayname + 1 = " << arrayname + 1 << endl;//arrayname + 1 = 008FFD78
    cout << "pas = " << pas << endl;//pas = 00CFFC94
    cout << "pas + 1 = " << pas + 1 << endl;//pas + 1 = 00CFFCA4
    /*
        11.指针与字符串
            在cout和多数C++表达式中,char数组名、char指针以及用括号括起的字符串常量(const char *)都被解释为字符串第一个字符的地址。
            用括号括起的字符串常量被存储在某一位置,如果将其赋值给指针,则该指针指向该字符串常量,该指针可以获取字符串到但是不可以更改字符串
            不应该对没有new分配内存的char指针执行cin操作,也不应该尝试获取野指针所指向的内容。
            如果给cout提供一个char *,则cout默认打印字符串;如果需要显示改字符串地址,则应该强制类型转换char * 为int *
            在为char *指针使用new分配内存时,可以根据字符串的长度为其分配内存,但是记得要给'\0'留空间噢。
    */
    cout << "指针与字符串*************************************************************" << endl;
    char var_char[5] = "Jasm";
    const char* p_varchar = "Booo";//不允许更改
    //根据字符串的长度为其分配内存
    char* p_new_var = new char[strlen("Jasm") + 1];//strlen()返回的是字符串的长度,不包含'\0'
    strcpy_s(p_new_var, 5, "Jasm");//使用深拷贝,如果是p_new_var = var_char的话是浅拷贝,则new的存储空间就用不上
    cout << "p_new_var = " << p_new_var << endl;//p_new_var = Jasm   ---  显示字符串
    cout << "p_new_var = " << (int*)p_new_var << endl;//p_new_var = 01140280   ---   显示地址
    delete[] p_new_var;
    /*
        12.指针new动态结构体
            创建结构体:见_7compound_types
            new结构体格式:inflatable * ps = new inflatable;//假设inflatable是我们创建的结构体
            使用指针访问new结构体的元素:箭头成员运算符(->),或者(*指针).成员。
    */
    cout << "指针new动态结构体*************************************************************" << endl;
    struct person {
        char name[20];
        char id[20];
    };
    person* Jasmine = new person{ "Jasmine","622827131099" };
    cout << "Jasmine->name = " << Jasmine->name << endl;//Jasmine->name = Jasmine
    cout << "(*Jasmine).id = " << (*Jasmine).id << endl;//(*Jasmine).id = 622827131099
    /*
        13.指针数组、数组指针、指针的指针
            指针数组:由指针组成的数组
            数组指针:指向数组的指针
    */
    cout << "指针数组、数组指针、指针的指针************************************************" << endl;
    person Booo = { "Booo","99999999" };
    person* p_Booo = &Booo;//这是一个数组指针
    person* p_person[3];//这是一个指针数组

    p_person[0] = &Booo;//数组指针的第一个元素指向了Booo
    p_person[1] = &Booo;//数组指针的第二个元素指向了Booo
    cout << "p_person[0]->name = " << p_person[0]->name << endl;//p_person[0]->name = Booo
    cout << "(*p_person)->id = " << (*p_person)->id << endl;//(*p_person)->id = 99999999
    person ** pp_person = p_person;//这是指向指针数组的指针
    person* (*p_p_person)[3] = &p_person;//这是指向 指针数组地址 的指针
    cout << "(**p_p_person)->name = " << (**p_p_person)->name << endl;//(**p_p_person)->name = Booo
    cout << "(*pp_person)->name = " << (*pp_person)->name << endl;//(*pp_person)->name = Booo
    cout << "(*(pp_person+1))->id = " << (*(pp_person + 1))->id << endl;//(*(pp_person+1))->id = 99999999

    return 0;
}
运行结果:
指针****************************************************************
sizeof(p_int) = 4
sizeof(p_char) = 4
love = 999999
p_int = 010FFD18
*p_int = 999999
new*******************************************************************
p_test[1] = 999
p_test = 01387280
p_test = 01387284
p_test[1] = 3
p_test = 01387280
p_test[1] = 999
p_double = 01384928
p_double = 01384930
p_double = 01384928
指针与数组*************************************************************
pointername = 010FFCC4
*(arrayname + 1) = 2
pointername = 010FFCC8
*(pointername + 1) = 4
(*pas)[2] = 3
sizeof(arrayname) = 16
sizeof(pointername) = 4
sizeof(pas) = 4
&arrayname = 010FFCC4
&arrayname + 1 = 010FFCD4
arrayname = 010FFCC4
arrayname + 1 = 010FFCC8
pas = 010FFCC4
pas + 1 = 010FFCD4
指针与字符串*************************************************************
p_new_var = Jasm
p_new_var = 01387248
指针new动态结构体*************************************************************
Jasmine->name = Jasmine
(*Jasmine).id = 622827131099
指针数组、数组指针、指针的指针************************************************
p_person[0]->name = Booo
(*p_person)->id = 99999999
(**p_p_person)->name = Booo
(*pp_person)->name = Booo
(*(pp_person+1))->id = 99999999

D:\Prj\_C++Self\_9pointer\Debug\_9pointer.exe (进程 17616)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
3.9 智能指针

由于程序员可能经常会忘记delete掉自己new的空间,由此导致的内存泄漏是很严重的问题,因此提出三种智能指针 auto_ptr, unique_ptr, shared_ptr:auto_ptr是C++98提出的,在C++11中取缔了。 智能指针是定义了模板类,该模板类可以的析构函数可以delete掉所指向的new的内存空间,以此达到精确管理内存的目的。 定义在memory头文件中,在std命名空间中。
3.9.1 智能指针模板类的定义

template<class X> class auto_ptr {
    public:
    explicit auto_ptr(X* p =0) throw();//throw()意味着不会引发异常
...};
3.9.2 智能指针的使用

auto_ptr<string> ps(new string); // ps an auto_ptr to string
// (use in place of string * ps)
unique_ptr<double> pdu(new double); // pdu an unique_ptr to double
shared_ptr<string> pss(new string); // pss a shared_ptr to string
注意事项
1.由于构造函数中explicit的限制,使用智能指针只能是显示初始化,因此不允许将指针赋值给智能指针 2.智能指针在通常情况下为指针的特点,可以使用*解除指针,也可以使用->访问结构体的元素,等等 3.不允许使用引用显式初始化指针
string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation); // NO!
3.9.3 智能指针分类

多个指针指向同一个内存空间释放内存时会引发异常,因此智能指针有对应的策略: auto_ptr and for unique_ptr:使用了所有权的概念,只允许一个指针拥有一个对象的所有权,当且仅当智能指针拥有该对象的所有权它才可以使用析构函数的delete该对象,他们拥有赋值转让所有权。 unique_ptr更严格,因为他不会轻易失能上一任智能指针,如果程序尝试将一个unique_ptr转让给另一个,则编译器允许在源对象为临时右值时允许,如果源对象具有一定的持续时间,则不允许。 auto_ptr不允许对容器对象使用,unique_ptr允许对容器对象使用。 为什么unique_ptr可以分辨安全或不安全的使用?因为它使用了移动构造函数和右值引用。 shared_ptr:创建一个更智能的指针,用于跟踪有多少智能指针引用特定对象。这称为引用计数。 std::move():将unique_ptr赋值给另一个unique_ptr。 注意事项: 1.auto_ptr和shared_ptr智能指向new分配的内存空间,不能指向new []分配的内存空间; 2.unique_ptr可以使用new和new []分配的内存空间。 3.std::unique_ptr< double[]>pda(new double(5)); // will use delete []
3.9.4 选择一个智能指针

如果需要多个指针指向一个对象,则使用shared_ptr。 如果不需要多个指针指向一个对象,则使用unique_ptr。
3.9.5 unique_ptr和shared_ptr之间的赋值

在源是一个右值时,允许将unique_ptr赋值给shared_ptr;原因是shared_ptr模板中包含了一个将unique_ptr或右值转换为shared_ptr的显式构造函数,shared_ptr接管以前unique_ptr的对象所有权。
3.9.6 举例

代码:
// str1.cpp -- introducing the string class
/*
Project name :          _20Smart_pointers
Last modified Date:     2022年4月5日17点33分
Last Version:           V1.0
Descriptions:           智能指针
*/
#include <iostream>
#include <string>
#include <memory>
using namespace std;
unique_ptr<string> demo(const char* s);
unique_ptr<int> make_int(int n);
class Report
{
private:
    std::string str;
public:
    Report(const std::string s) : str(s)
    {
        std::cout << "Object created!\n";
    }
    ~Report() { std::cout << "Object deleted!\n"; }
    void comment() const { std::cout << str << "\n"; }
};
int main()
{
    cout << "auto_ptr, unique_ptr, shared_ptr*****************************************************" << endl;
    {
        std::auto_ptr<Report> ps(new Report("using auto_ptr"));
        ps->comment(); // use -> to invoke a member function
    }
    {
        std::shared_ptr<Report> ps(new Report("using shared_ptr"));
        ps->comment();
    }
    {
        std::unique_ptr<Report> ps(new Report("using unique_ptr"));
        ps->comment();
    }
    cout << "多个指针指向统一内存空间,各智能指针的策略**************************************************" << endl;
    shared_ptr<string> films[5] =
    {
        unique_ptr<string>(new string("Fowl Balls")),
        unique_ptr<string>(new string("Duck Walks")),
        unique_ptr<string>(new string("Chicken Runs")),
        unique_ptr<string>(new string("Turkey Errors")),
        unique_ptr<string>(new string("Goose Eggs"))
    };
    //此处使用auto_ptr会引发错误
    //auto_ptr<string> pwin;
    shared_ptr<string> pwin;
    pwin = films[2]; // films[2] loses ownership
    cout << "The nominees for best avian baseball film are\n";
    for (int i = 0; i < 5; i++)
        cout << *films << endl;
    cout << "The winner is " << *pwin << "!\n";
    //对于auto_ptr,#3是允许的,但是程序员可能不小心使用p1,但是p1对于该对象没有权限,会导致错误,这也是取缔auto_ptr的原因
    auto_ptr<string> p1(new string("auto")); //#1
    auto_ptr<string> p2; //#2
    p2 = p1; //#3
    //cout << *p1;//这句会出错

    //对于unique_ptr,就直接不允许#6,所以更安全
    unique_ptr<string> p3(new string("auto")); //#4
    unique_ptr<string> p4; //#5
    //p4 = p3; //#6---这句会出错
    //但是对于以下这种情况是允许的,因为临时智能指针temp在函数运行结束时会删除对象,不会导致auto_ptr以上出现的问题。
    unique_ptr<string> ps;
    ps = demo("Uniquely special");//allowed
    cout << *ps<<endl;//Uniquely special
    unique_ptr<string> pu3;
    pu3 = unique_ptr<string>(new string("Yo!")); //#2 allowed
    //std::move()
    unique_ptr<string> ps1, ps2;
    ps1 = demo("Uniquely special");
    ps2 = move(ps1); // enable assignment
    ps1 = demo(" and more");
    cout << *ps2 << *ps1 << endl;//Uniquely special and more

    unique_ptr<int> pup(make_int(rand() % 1000)); // ok
    //shared_ptr<int> spp(pup); // not allowed, pup an lvalue
    shared_ptr<int> spr(make_int(rand() % 1000)); // ok

    return 0;
}

unique_ptr<string> demo(const char* s)
{
    unique_ptr<string> temp(new string(s));
    return temp;
}
unique_ptr<int> make_int(int n)
{
    return unique_ptr<int>(new int(n));
}
运行结果:
auto_ptr, unique_ptr, shared_ptr*****************************************************
Object created!
using auto_ptr
Object deleted!
Object created!
using shared_ptr
Object deleted!
Object created!
using unique_ptr
Object deleted!
多个指针指向统一内存空间,各智能指针的策略**************************************************
The nominees for best avian baseball film are
Fowl Balls
Duck Walks
Chicken Runs
Turkey Errors
Goose Eggs
The winner is Chicken Runs!
Uniquely special
Uniquely special and more

D:\Prj\_C++Self\_20Smart_pointers\Debug\_20Smart_pointers.exe (进程 5256)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
README

此为本人读C++ Primer总结的笔记,如有错误或知识缺口,请在评论区告知。如本文有在实践中帮到您,是本人的荣幸。
回复

使用道具 举报

3

主题

11

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2025-3-24 06:42:02 | 显示全部楼层
楼下的接上
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表