Javer 学 c++(八):指针篇

总览

主要讲了 c++ 中指针的概念、定义和使用、const以及与函数、数组的使用关系

指针的基本概念

作用:可以通过指针间接访问内存

  • 内存的编号是从 0 开始记录的,一般用 16 进制数字表示
  • 可以利用指针变量保存地址

指针变量的定义和使用

指针变量定义语法:数字类型 * 变量名

c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    int a = 10;
    // 1. 定义指针
    int * p;
    // 将指针指向 a 的地址
    p = &a;

    // 2. 使用指针:解引用(指针前加*号)
    // 通过解引用方式来找到指针指向的内存,找到指针指向内存中的数据
    *p = 1000;
    // 结果为两个 1000,即已经将0x16f433018地址的数修改了
    cout << a << endl;
    cout << *p << endl;

    return 0;
}

指针所占内存空间

指针也是一种数据类型,那么指针占用的内存空间是多大呢?

c++ 复制代码
#include <iostream>
using namespace std;
#include "swap.h"

int main() {

    int a = 10;
    // 1. 定义指针
    int * p;
    // 将指针指向 a 的地址
    p = &a;

    // 64 位系统 8 字节
    // 若是 32 位则为 4 字节
    cout << sizeof(p) << endl;

    return 0;
}

空指针和野指针

空指针

定义:指针变量指向内存中编号为 0 的空间

用途:初始化指针变量

重点:空指针指向的内存是不可访问的

c++ 复制代码
#include <iostream>
using namespace std;
#include "swap.h"

int main() {

    // 1. 定义指针
    int * p = NULL;

    *p = 100;
    // 内存编号 0-255 为系统占用内存,不允许用户访问
    cout << *p << endl;

    return 0;
}

野指针

指针变量指向非法的内存空间

c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    // 指针变量 p 指向内存地址编号为 0x1100 的空间
    int * p = (int *)0x1100;

    // 访问野指针报错
    cout << *p << endl;

    return 0;
}

重点:空指针和野指针本质上都不是我们自己申请的空间,不要随意的去访问它

const 修饰指针

const 修饰指针:常量指针

特点:指针的指向可以修改,但是指针指向的值不可以修改

  • 常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改
  • 常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值
  • 指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址
c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    int a = 10;
    int b = 20;
    // 常量指针
    const int * p = &a;

    a = 20; // ok,仍然可以通过原来的声明修改值
    // *p = 20; Error,*p 是const int的,不可修改,即常量指针不可修改其指向地址
    p = &b; //OK,指针还可以指向别处,因为指针只是个变量,可以随意指向;

    return 0;
}

const 修饰常量:指针常量

本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。

  • 它是个常量!
  • 指针所保存的地址可以改变,然而指针所指向的值却不可以改变
  • 指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    int a = 10;
    int b = 20;
    // 常量指针
    int * const p = &a;

    a = 20; // OK,仍然可以通过原来的声明修改值
    *p = 20; // OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
    // p = &b; Error,因为p2是const 指针,因此不能改变p2指向的内容

    return 0;
}

const 修饰指针和常量

指针就是一个常量,且它指向的对象也是一个常量。

c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    int a = 10;
    int b = 20;
    // 常量指针
    const int * const p = &a;

    a = 20; // OK
    //*p = 20; ERROR
    //p = &b; Error

    return 0;
}

技巧:看 const 后面修饰的是指针(*) 还是常量(p),如果修饰的是指针,那就是常量指针;修饰的是常量,那就是指针常量

指针和数组

作用:利用指针访问数组元素

c++ 复制代码
#include <iostream>
using namespace std;

int main() {

    //利用指针访问数组
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 第一个元素
    int * p = arr; // arr 就是数组首地址
    cout << *p << endl;

    // 第二个元素
    // 因为数组里存放的是 int,所以只需要指针向后移动 4 个字节即可
    // 同时指针也是一个 int 类型,所以 p++ 就是让指针向后移动 4 个字节
    p++;
    cout << *p << endl;

    // 遍历
    int * p2 = arr;
    for (int i = 0; i < 10; i++) {
        cout << *p2++ << endl;
    }

    return 0;
}

指针和函数

作用:利用指针做函数参数,可以修改实参的值

值传递

前面我们讲到了 c++是值传递,形参的改变不会影响形参的值,这就是值传递

c++ 复制代码
#include <iostream>
using namespace std;

// 返回值类型 函数名(参数列表)
void swap(int a, int b) {
    int tmp = a;
    a = b;
    b = tmp;
}

int main() {

    int a = 10;
    int b = 20;
    swap(a, b);
    // 因为是值传递,所以a=10,b=20没有变化
    cout << a << b << endl;

    return 0;
}

地址传递

c++ 复制代码
#include <iostream>
using namespace std;

// 返回值类型 函数名(参数列表)
void swap(int *p1, int *p2) {
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

int main() {

    int a = 10;
    int b = 20;
    swap(&a, &b);
    // 因为是地址传递,所以a=20,b=10已经交换
    cout << a << b << endl;

    return 0;
}

本质上地址传递也是值传递,因为传递的是地址的值

课后作业

描述:封装一个函数,利用冒泡排序,实现对整数数组的排序

c++ 复制代码
#include <iostream>
using namespace std;

// 这么传 int arr[] 也可以
void bubble_sort(int * arr, int size) {
    for (int i = 0; i < size-1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

void printArray(int * arr, int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
}

int main() {

    int arr[9] = { 9, 8, 5, 4, 3, 6, 1, 7, 2 };

    // 预计算数组长度
    int size = sizeof(arr) / sizeof(arr[0]);

    bubble_sort(arr, size);

    printArray(arr, size);

    return 0;
}

如何将数组作为参数传入?

  • 传入数组的引用
  • 传入数组及其大小
相关推荐
何中应7 小时前
Spring Boot单体项目整合Nacos
java·spring boot·后端
Juchecar7 小时前
解决Windows下根目录运行 pnpm dev “无法启动 Vite 前端,只能启动 Express 后端”
前端·后端·node.js
奕川7 小时前
Colima + nerdctl 零基础教程:告别 Docker Desktop,拥抱轻量容器世界
后端
dylan_QAQ7 小时前
Java转Go全过程01-基础语法部分
java·后端·go
追逐时光者7 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 52 期(2025年8.25-8.31)
后端·.net
一语长情8 小时前
RocketMQ 消息队列冷读问题的分析与优化
java·后端·架构
IT_陈寒8 小时前
SpringBoot性能翻倍秘籍:5个90%开发者不知道的JVM调优实战技巧
前端·人工智能·后端
再吃一根胡萝卜9 小时前
🔐 跨域(CORS)与 CSRF:你以为的安全,真的安全吗?
后端