C++-指针

在C++中,指针是至关重要的组成部分。它是C++语言最强大的功能之一,也是最棘手的功能之一。 指针具有强大的能力,其本质是协助程序员完成内存的直接操纵。

指针:特定类型数据在内存中的存储地址,即内存地址。

指针变量的定义语法: 先声明,后赋值:

变量类型 * 指针变量名;       // 声明
指针变量名 = 内存地址值;     // 赋值

int num = 10;
int * p;		// 声明
p = #	// 赋值
  • 变量类型(如上int)表示,指针(内存地址)指向的内存区域,存放的是整型数据

  • *符号有两种含义:

  • 声明时:*p,表示变量p,是指针变量(存的是内存地址)

  • 使用时:*p,表示取指针p执行内存区域的数据

  • &符号表示取变量内存地址,是一个取内存地址的单目操作符

野指针和空指针

野指针:被声明但未初始化(赋值)的指针。这个指针会指向随机的内存空间,可能导致未知问题。

int * p;    // 声明指针(分配了8字节空间), p是野指针因为未被赋值
*p = 10;    // 将10赋予指针p所指向的空间

*p = 10;是向未知的、随机的4字节内存区域,修改存储值为10

为避免野指针,应养成良好的变成习惯,及时初始化,或将指针置为空指针更为安全

int * p = NULL;
int * p = nullptr;

指针运算

int num = 10;
int *p = #
cout << p << endl;    // 结果:0x20d1ff6e4;
cout << p++ << endl;    // 结果:0x20d1ff6e8

指针进行加减运算的结果,和指针指向内存区域的数据类型有关,以加法为例:

char 类型指针 +1, 地址+1 (字节)

int 类型指针+1, 地址+4(字节)

double 类型指针+1, 地址+8 (字节)

指针运算

数组对象本身记录的是内存地址(第一个元素地址) 可以通过指针运算,完成使用指针存取数组元素。

int v[] = {1, 2, 3, 4, 5};
int *p = v;
*p = 11;                // 赋值数组第一个元素
*(p+1) = 22;            // 赋值数组第二个元素
*(p+2) = 33;            // 赋值数组第三个元素
cout << *p << endl;     // 取数组第一个元素
cout << *(p+1) << endl; // 取数组第二个元素
cout << *(p+2) << endl; // 取数组第三个元素

动态内存分配

动态内存分配:即由程序员手动的进行内存空间的分配、内存空间的释放等内存管理操作。

C++代码中,变量、数组等对象的创建,是由C++自动分配内存的,称之为(自动)静态内存分配。

(自动)静态内存管理,是不会进行内存空间的自动清理的。(无垃圾回收机制) 我们需要手动的管理内存,即手动分配,用完清理。

手动管理方式:

new运算符申请空间,提供该空间的指针(地址)

delete运算符申请的空间,仅用于new申请的空间

建议:写完new后,立刻写delete,然后再写业务逻辑代码

int * p = new int;      // 申请int类型(4字节)空间
int * p = new double;   // 申请double类型(8字节)空间
delete p;               // 删除申请的空间
int * p = new int[5];   // 申请5元素int数组空间
delete[] p;             // 删除申请的5元素数组空间

优势: 手动控制内存,避免内存空间浪费

劣势: 考验程序员水平,用的好效率高,用不好有反效果

new

new运算符用于申请并分配内存空间 并提供指向该空间的指针(内存地址) 基本语法:

new type 申请普通变量空间

new type[n] 申请数组空间

delete

delete运算符用于释放内存 仅可用于new运算符申请的内存区域

基本语法:

delete 指针 删除普通变量空间

delete[] 指针 删除数组空间

数组元素的插入举例:

#include "iostream"
using namespace std;

/* 在下标1和3插入数字:11和66 */

int main()
{
    // 示例数组
    int * pArr = new int[5] {1, 3, 5, 7, 9};

    // 创建新数组
    int * pNewArr = new int[7];

    // 循环新数组,挨个进行元素填充(非插入的位置,填充老数组元素,插入位置填充新元素)
    int offset = 0;     // 偏移量用来控制新老数组元素的下标对照
    for (int i = 0; i < 7; i++)
    {
        if (i == 1)
        {
            // 走到了下标1,应当执行新元素插入
            pNewArr[i] = 11;
            offset++;       // 每插入一个新元素,offset+1
            continue;
        } else if (i == 3)
        {
            // 走到了下标3,应当执行新元素插入
            pNewArr[i] = 66;
            offset++;       // 每插入一个新元素,offset+1
            continue;
        }

        // 不是插入位置,从老数组中提取元素放入新数组中
        // 公式:老数组的元素下标 + offset = 新数组元素下标
        // 当前循环的i是新数组的元素下标
        pNewArr[i] = pArr[i-offset];
    }

    // 收尾工作
    delete[] pArr;
    pArr = pNewArr;

    // 将新数组的内容输出
    for (int i = 0; i< 7; i++)
    {
        cout << pNewArr[i] << ",";
    }

    return 0;
}

数组元素的删除举例:

#include "iostream"
using namespace std;

int main()
{
    // 示例数组
    int * pArr = new int[5] {1, 3, 5, 7, 9};

    // 创建一个新的数组,将需要保留的复制到新数组中
    int * pNewArr = new int[4];

    // 循环去遍历老的数组,将需要的元素放入新数组中(不要的要跳过)
    for (int i = 0; i < 5; i++)
    {
        if (i == 2)
        {
            continue;
        }

        if (i > 2)
        {
            pNewArr[i-1] = pArr[i];
        }else
        {
            pNewArr[i] = pArr[i];
        }
    }

    // 可选
    delete[] pArr;      // 回收老数组的空间(可选,根据需要来说)

    // 可选
    pArr = pNewArr;     // 将老数组的指针指向新数组的内存空间(可选,根据需要)

    for (int i = 0; i < 4; i++)
    {
        cout << "新数组的元素是:" << pNewArr[i] << endl;
        cout << "新数组的元素是:" << pArr[i] << endl;
    }

    return 0;
}

指针悬挂:

指针指向区域已经被回收(delete),这种问题称之为:指针悬挂。

不要轻易进行指针之间相互赋值

delete回收空间前,确保此空间100%不再被使用

错误示范:

int main()
{   int * p1 = new int;
    int * p2 = p1;  // 将p1赋值给p2
    *p1 = 10;   
    delete p1;
    cout << "p2指针记录的是:" << *p2 << endl;
    return 0;
}

const指针

const是C++关键字,被译为常量,const指针即表示:常量指针。

const用来修饰常量(不可更改),可以配合指针使用 以int为例。

const int * p; 指向常量的指针,即存储值不可变,但指针可修改指向

int * const p = 地址; 常量指针

存储的值可以变 指着不可修改指向 必须初始化指针的地址

const int * const p = 地址; 指向常量的常量指针

存储的值和指针的指向,均不可修改

相关推荐
小志biubiu21 分钟前
【C++11】可变参数模板/新的类功能/lambda/包装器--C++
开发语言·c++·笔记·学习·c++11·c11
新知图书25 分钟前
Rust编程与项目实战-模块std::thread(之二)
开发语言·rust
羑悻的小杀马特26 分钟前
位图与布隆过滤器
数据结构·c++·位图·布隆过滤器
是店小二呀28 分钟前
【C++】C++11新特性详解:可变参数模板与emplace系列的应用
android·c++
WALL-EC34 分钟前
VS2019+QT5.12 创建UI(Dialog窗体)界面自动生成.h和cpp文件
开发语言·qt·ui
sususugaa36 分钟前
前端框架Vue3——响应式数据,v-on,v-show和v-if,v-for,v-bind
开发语言·前端·vue.js·前端框架
豪宇刘1 小时前
Gradle核心概念总结
开发语言·gradle
想成为高手4991 小时前
深入理解二叉搜索树(BST)
数据结构·c++·算法
薯条不要番茄酱1 小时前
【JavaEE初阶】枫叶经霜艳,梅花透雪香-计算机是如何运行的?
java·开发语言·后端·java-ee·学习方法
闻缺陷则喜何志丹2 小时前
【C++数论 因数分解】829. 连续整数求和|1694
c++·数学·算法·力扣··因数分解·组数