18.指针的进阶使用

18.指针-指针的进阶使用


18.1 知识点

指针变量所占内存空间

不管何种变量类型的指针变量,所占的内存空间都是一样的,因为它是用来存储变量的内存地址的(存储内存单元的门牌号码)。门牌号码是有范围限制的,不需要因为变量类型的不同而改变大小。

在不同位数的操作系统和不同编译器设置上,指针所占的字节数有所不同:

  • 32位:4字节
  • 64位:8字节(常见)
int a = 1;
int* p = &a;
cout << sizeof(p) << endl; // 8

char c = 'A';
char* p_c = &c;
cout << sizeof(p_c) << endl; // 8

bool b = true;
bool* p_b = &b;
cout << sizeof(p_b) << endl; // 8

&(地址符号) 和 *(解引用操作符) 的混合使用

回顾

int d = 1;
// & 地址符号 主要用于获取变量地址,用于指针赋值
int* p2 = &d;
cout << p2 << endl; // 0000007E06B2F504

// * 解引用操作符 主要用于获取地址中存储的内容
cout << *p2 << endl; // 1

// 获取指针变量的地址
cout << &p2 << endl; // 0000007E06B2F528

&* 和 *& 混合使用

  1. & 使用*

    使用时从右向左开始结合,表示先获取指针指向地址的值,再获取该值的地址,相当于获取指针指向的内存地址。

    int* pp = &*p2; // *p2得到d,&d得到d的地址,赋值给pp
    cout << p2 << endl; // 0000007E06B2F504
    cout << pp << endl; // 0000007E06B2F504
    cout << &p2 << endl; // 0000007E06B2F528
    cout << &pp << endl; // 0000007E06B2F548
    
  2. ***& 使用**

    使用时从右向左开始结合,表示先获取变量的地址,再获取该地址中存储的值,相当于是获取一个变量的值。

    cout << *&d << endl; // 1
    // &d得到d的地址,*d的地址得到指向该地址的值,即d的值
    

    *& 是可以和指针变量配合使用的。相当于先得到指针自己的地址,然后再通过这个地址得到其中存储的内容——指针存储的地址值。

    cout << &d << endl; // 0000007E06B2F504 d的地址
    cout << *&p2 << endl; // 0000007E06B2F504 p2存着d的地址,&p2是p2的地址,*p2的地址得到的是p2存着的地址,即d的地址
    cout << p2 << endl; // 0000007E06B2F504 p2存着d的地址
    

注意

只要记住了 *& 两个符号的作用,那么在处理混合使用时,就可以分开来看,从右往左看即可。

  • * 获取地址中存储的变量的值
  • & 获取变量的地址

指针的自增减运算

指针是可以做自增减运算的。由于指针存储的是变量的地址,当我们做指针自增减运算时,相当于是对地址进行运算,并且地址的变化是遵循以下规律的:

  • 自增:将指针向前移动一个元素的大小
  • 自减:将指针向后移动一个元素的大小

元素大小:指针变量的类型决定,sizeof(指针变量类型) 得到的就是元素大小。

举例说明

int 是 4 字节,每次自增都会让地址跳跃 4 字节。

int bb = 10;
int* ptrbb = &bb;

cout << ptrbb << endl; // 000000EEE90FF7D4

ptrbb++;
cout << ptrbb << endl; // 000000EEE90FF7D8

++ptrbb;
cout << ptrbb << endl; // 000000EEE90FF7DC

ptrbb = ptrbb + 1;
cout << ptrbb << endl; // 000000EEE90FF7E0

ptrbb = ptrbb - 3;
cout << ptrbb << endl; // 000000EEE90FF7D4

空类型指针

空类型指针是一种特殊的指针,它表示不特定类型的指针。指针变量可以指向任意的变量类型,具体存放何种类型由后续赋值决定。赋值后,如果需要使用需要强转为对应的数据类型。

void* pVoid;
short s = 55;
pVoid = &s;
cout << pVoid << endl; // 00000044C7CFFA74

short s2 = *(short*)pVoid;
cout << s2 << endl; // 55

18.2 知识点代码

Lesson18_指针_指针的进阶使用.cpp

#include <iostream>
using namespace std;
int main()
{
    std::cout << "指针的进阶使用\n";

    #pragma region 知识点一 指针变量所占内存空间

    //不管何种变量类型的指针变量
    //所占的内存空间都是一样的
    //因为它是用来存储变量的内存地址的(存储内存单元的门牌号码)
    //门牌号码是有范围限制的
    //不需要因为变量类型的不同而改变大小
    //在不同位数的操作系统 和 不同编译器设置上
    //指针所占的字节数有所不同
    //32位:4字节
    //64位:8字节(常见)
    int a = 1;
    int* p = &a;
    cout << sizeof(p) << endl;//8

    char c = 'A';
    char* p_c = &c;
    cout << sizeof(p_c) << endl;//8

    bool b = true;
    bool* p_b = &b;
    cout << sizeof(p_b) << endl;//8

    #pragma endregion

    #pragma region 知识点二 &(地址符号) 和 *(解引用操作符) 的混合使用

    //回顾
    int d = 1;
    //& 地址符号 主要用于获取变量地址 用于指针赋值
    int* p2 = &d;
    cout << p2 << endl;//0000007E06B2F504
    //* 解引用操作符 主要用于获取地址中存储的内容
    cout << *p2 << endl;//1
    // 获取指针变量的地址
    cout << &p2 << endl;//0000007E06B2F528


    //& * 这两个符号是可以混合使用的
    //主要用法有两种
    //1.&*
    //  使用时从右向左开始结合
    //  表示先获取指针指向地址的值,再获取该值的地址
    //  相当于获取指针指向的内存地址
    int* pp = &*p2;//*p2得到d &d得到d的地址 赋值给pp
    //两个指针指向的地址是一样的 都是指向的
    cout << p2 << endl;//0000007E06B2F504
    cout << pp << endl;//0000007E06B2F504
    //两个指针变量指向的地址是不一样的 在两块内存空间
    cout << &p2 << endl;//0000007E06B2F528
    cout << &pp << endl;//0000007E06B2F548

    //&* 只能和指针变量配合使用
    //&*d;//报错 &*只能用于指针变量

    //2.*&
    //  使用时从右向左开始结合
    //  表示先获取变量的地址,再获取该地址中存储的值
    //  相当于是获取一个变量的值
    cout << *&d << endl;//1 &d得到d的地址 *d的地址 得到指向该地址的值 该地址指向d 即d的值

    //*&是可以和指针变量配合使用的
    //相当于先得到指针自己的地址,然后再通过这个地址得到其中存储的内容
    //指针存储的地址值
    cout << &d << endl;//0000007E06B2F504 d的地址
    cout << *&p2 << endl;//0000007E06B2F504 p2存着d的地址 &p2是p2的地址 *p2的地址 得到的是p2存着的地址 即d的地址
    cout << p2 << endl;//0000007E06B2F504 p2存着d的地址

    //注意:
    //只要记住了*和&两个符号的作用
    //那么在处理混合使用时,就分开看就可以了,从右往左看即可
    //*获取地址中存储的变量的值
    //&获取变量的地址的

    #pragma endregion

    cout << "***************" << endl;

    #pragma region 知识点三 指针的自增减运算

    //指针是可以做自增减运算的
    //由于指针存储的是变量的地址
    //当我们做指针自增减运算时
    //相当于是对地址进行运算
    //并且地址的变化是遵循以下规律的
    // 自增:将指针向前移动 一个元素的大小
    // 自减:将指针向后移动 一个元素的大小
    // 元素大小:指针变量的类型决定,sizeof(指针变量类型)得到的就是元素大小
    //
    // 举例说明:int是4字节 以下每次自增都是让地址跳跃4字节

    int bb = 10;
    int* ptrbb = &bb;

    cout << ptrbb << endl;//000000EEE90FF7D4

    ptrbb++;
    cout << ptrbb << endl;//000000EEE90FF7D8

    ++ptrbb;
    cout << ptrbb << endl;//000000EEE90FF7DC

    ptrbb = ptrbb + 1;
    cout << ptrbb << endl;//000000EEE90FF7E0

    ptrbb = ptrbb - 3;
    cout << ptrbb << endl;//000000EEE90FF7D4

    #pragma endregion

    cout << "***************" << endl;

    #pragma region 知识点四 空类型指针

    //空类型指针是一种特殊的指针
    //它表示不特定类型的指针
    //指针变量可以指向任意的变量类型
    //具体存放何种类型,由后续赋值决定
    //赋值后,如果需要使用需要强转为对应的数据类型

    void* pVoid;
    short s = 55;
    pVoid = &s;
    cout << pVoid << endl;//00000044C7CFFA74
    short s2 = *(short*)pVoid;
    cout << s2 << endl;//55

    #pragma endregion
}

18.3 练习题

指针占用多少内存空间?

int i = 10;
int* p = &i;
cout << sizeof(p) << endl; // 8 在64位操作系统,32位是4

指针自增减的规则是什么?

p++ ++p p– –p p += 3 p -= 2 p = p + n
会按照元素所占内存空间来进行门牌地址的偏移,例如整型每加一次加4个字节

请说明以下代码中打印结果的含义

注意:不要通过敲代码验证,直接回答

int a = 10;
int* p = &a;
cout << *&p << endl;
int a = 10;
int* p = &a;
cout << *&p << endl; // a的地址
cout << p << endl; // a的地址
cout << &a << endl; // a的地址

请说明以下代码中可能存在的问题

注意:不要通过敲代码验证,直接回答

int a = 70000;
int* p = &a;
cout << *(short*)p << endl;

代码中先将 int 类型的指针 p 强制转换为 short* 类型的指针,然后进行解引用操作。
int 类型通常占用 4 个字节(具体取决于编译器和系统,但一般为 4 字节),而 short 类型通常占用 2 个字节。
当 int 类型的变量 a 的值为 70000 时,其在内存中的二进制表示是特定的,将 int* 指针转换为 short* 指针并解引用,会导致只取到 a 的低 2 字节数据。
对于 70000,其十六进制表示为 0x11170,将其存储在内存中,低 2 字节的数据为 0x170,转换为十进制就是 4464。
这种强制类型转换会造成数据截断,丢失了 a 的高 2 字节信息,可能导致结果不符合预期。

int a = 70000;
int* p = &a;
cout << *(short*)p << endl; // 4464

18.4 练习题代码

Lesson18_练习题.cpp

#include <iostream>
using namespace std;
int main()
{
    std::cout << "指针的更多使用 练习题\n";

    #pragma region 练习题一

    //指针占用多少内存空间?

    //int i = 10;
    //int* p = &i;
    //cout << sizeof(p) << endl;//8 在64位操作系统 32位是4 

    #pragma endregion

    #pragma region 练习题二

    //指针自增减的规则是什么?

    //p++ ++p p-- --p  p += 3  p -= 2  p = p + n
    //会按照元素所占内存空间来进行门牌地址的偏移 例如整形每加一次加4个字节

    #pragma endregion

    #pragma region 练习题三

    /*请说明以下代码中打印结果的含义
    注意:不要通过敲代码验证,直接回答
    int a = 10;
    int* p = &a;
    cout << *&p << endl;*/

    //int a = 10;
    //int* p = &a;
    //cout << *&p << endl;//a的地址
    //cout << p << endl;//a的地址
    //cout << &a << endl;//a的地址

    #pragma endregion

    #pragma region 练习题四

     /*请说明以下代码中可能存在的问题
     注意:不要通过敲代码验证,直接回答
     int a = 70000;
     int* p = &a;
     cout << *(short*)p << endl;*/

    int a = 70000;
    int* p = &a;
    cout << *(short*)p << endl;//4464
    //代码中先将 int 类型的指针 p 强制转换为 short* 类型的指针,然后进行解引用操作。
    //int 类型通常占用 4 个字节(具体取决于编译器和系统,但一般为 4 字节),而 short 类型通常占用 2 个字节。
    //当 int 类型的变量 a 的值为 70000 时,其在内存中的二进制表示是特定的,将 int* 指针转换为 short* 指针并解引用,会导致只取到 a 的低 2 字节数据。
    //对于 70000,其十六进制表示为 0x11170,将其存储在内存中,低 2 字节的数据为 0x170,转换为十进制就是 4464。
    //这种强制类型转换会造成数据截断,丢失了 a 的高 2 字节信息,可能导致结果不符合预期。

    #pragma endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏