前端学C++可太简单了:指针

理解c++指针前,先理解c++引用,因为引用的语法更现代化,更好理解。指针是c语言里的,c++兼容c,所以继承了指针。

1. 指针 vs 引用

引用

cpp 复制代码
int original = 100;
int& ref = original;  // ref是original的别名

ref = 200;           // 直接修改,original也变成200
// 引用必须在声明时初始化,且不能再指向其他变量

指针

cpp 复制代码
int original = 100;
int* ptr = &original; // ptr指向original的地址,&在这里是取址操作符取original的地址

*ptr = 200;          // 通过解引用修改original
ptr = &other;        // 指针可以重新指向其他变量
ptr = nullptr;       // 指针可以为空

JavaScript类比

js 复制代码
// 引用类似于const声明的对象引用
const obj = { value: 100 };
obj.value = 200;  // 可以修改内容,但obj永远指向同一个对象

// 指针类似于let声明的变量
let objPtr = { value: 100 };
objPtr.value = 200;      // 修改内容
objPtr = { value: 300 }; // 可以指向新对象
objPtr = null;           // 可以为空

2. 指针的语法

2.1 指针的基本概念

指针是存储内存地址的变量,它"指向"另一个变量的内存位置。

cpp 复制代码
// 基本语法:类型* 指针名;

int* ptr;        // 声明一个指向int的指针
float* fptr;     // 声明一个指向float的指针
char* cptr;      // 声明一个指向char的指针

2.2 指针的声明和初始化

cpp 复制代码
// 方式1:声明后赋值
int x = 42;
int* ptr;        // 声明指针
ptr = &x;        // 使用取地址符&获取x的地址

// 方式2:声明时初始化
int y = 100;
int* ptr2 = &y;  // 声明并初始化

// 方式3:初始化为空指针
int* ptr3 = nullptr;  // C++11推荐方式
int* ptr4 = NULL;     // 传统方式(不推荐)

2.3 指针的解引用

cpp 复制代码
int value = 42;
int* ptr = &value;

// 解引用:使用*操作符获取指针指向的值
int result = *ptr;   // result = 42

// 通过指针修改值
*ptr = 100;          // 现在value = 100

2.4 指针的成员访问

cpp 复制代码
// 假设有一个类
class Point {
public:
    int x, y;
    void print() { /* ... */ }
};

Point obj = {10, 20};
Point* ptr = &obj;

// 方式1:箭头操作符 -> (推荐)
ptr->x = 30;         // 相当于 (*ptr).x = 30
ptr->print();        // 相当于 (*ptr).print()

// 方式2:解引用后用点操作符
(*ptr).x = 30;       // 等价于上面的写法
(*ptr).print();

2. 引用的底层实现就是指针

编译器视角

cpp 复制代码
int value = 42;
int& ref = value;

// 底层实现类似于:
int* __hidden_ptr = &value;  // 编译器生成的隐藏指针
// 当你写 ref 时,编译器自动替换为 (*__hidden_ptr)

汇编代码证明

cpp 复制代码
int x = 10;
int& ref = x;
int* ptr = &x;

ref = 20;    // 编译成:mov [address], 20
*ptr = 20;   // 编译成:mov [address], 20
// 生成的汇编代码几乎相同!

引用 = 受限制的指针

cpp 复制代码
// 引用就是一个有以下限制的指针:
// 1. 不能为nullptr
// 2. 不能重新指向  
// 3. 自动解引用
// 4. 不能做指针运算

class SafePointer {  // 引用的概念模型
private:
    int* ptr;        // 底层还是指针

public:
    SafePointer(int& target) : ptr(&target) {}  // 必须绑定到对象

    int& operator=(int value) {
        *ptr = value;  // 自动解引用
        return *this;
    }

    operator int&() {
        return *ptr;   // 自动转换为引用
    }

    // 禁止的操作:
    // SafePointer() = delete;              // 不能默认构造
    // void operator=(SafePointer&) = delete; // 不能重新绑定
    // void operator++() = delete;          // 不能指针运算
};

引用与指针的相互转换

cpp 复制代码
// 这两种写法在底层是相似的:

// 方式1:使用引用
void updateCamera(Camera& camera) {
    camera.position.z = 5;  // 编译器:(*hidden_ptr).position.z = 5
}

// 方式2:使用指针
void updateCamera(Camera* camera) {
    camera->position.z = 5; // 显式:(*camera).position.z = 5
}

// 调用方式
Camera myCamera;
updateCamera(myCamera);   // 编译器:updateCamera(&myCamera)
updateCamera(&myCamera);  // 显式传递地址
cpp 复制代码
int value = 100;

// 引用写法:
int& ref = value;
ref = 200;               // 看起来像赋值
cout << ref;             // 看起来像变量

// 等价的指针写法:
int* ptr = &value;
*ptr = 200;              // 显式解引用
cout << *ptr;            // 显式解引用

// 底层都是对同一块内存的操作!

3. 内存地址的理解

想象计算机内存像一个巨大的公寓楼:

cpp 复制代码
int age = 25;        // 在1001号房间放了数字25
int* p = &age;       // p这个变量存储的是"1001"这个房号

cout << age;         // 直接看1001号房间:25
cout << p;           // 看p存储的房号:1001
cout << *p;          // 通过房号1001去看房间内容:25

*p = 30;            // 通过房号1001,把房间内容改为30
cout << age;        // 现在1001号房间的内容:30
js 复制代码
// JavaScript隐式管理这些"房号"
let obj = { age: 25 };  // JS引擎自动分配"房号",你看不到
let ref = obj;          // ref也指向同一个"房号"

指针和引用在大多数情况下是可以理解成一样的,为什么c++在设计时不把指针和引用结合起来,只用一种概念?导致现在代码中一下用指针一下用引用,阅读理解起来麻烦

4. c++中共存指针、引用的原因

历史原因:c++的两个父亲

cpp 复制代码
// C++继承了C的指针
int* ptr = &value;  // 来自C语言的遗产
// 第一个父亲是C,要兼容C程序必须要有C语法

// C++后来添加了引用
int& ref = value;   // C++的改进
// 第二个父亲是C++语言设计者,引入,改进自己的语法

C在那个时代可是很牛逼的存在,程序都是基于C写的,可能由于C的不好用,才出现了C++,但是C++又不能立马改革,替换掉C,要想C++得到发展就得兼容C,会写C的人也能立马会写C++,之前写的C程序也能在C++里跑,这样可以慢慢的指引C开发人员往C++靠,程序也渐渐的从C变成C++,然后经过40年的发展,C/C++程序多的数不胜数了,这些程序要继续工作C++必须向后兼容,如果是新语言可以从零开始设计,比如后面出现的Java/C#,JavaScript,Rust中没有指针(以上都是我个人的臆想,没有依据,乐呵一下就好)

相关推荐
NuyoahC几秒前
笔试——Day8
c++·算法·笔试
军军君019 分钟前
基于Springboot+UniApp+Ai实现模拟面试小工具三:后端项目基础框架搭建上
前端·vue.js·spring boot·面试·elementui·微信小程序·uni-app
布丁05239 分钟前
DOM编程实例(不重要,可忽略)
前端·javascript·html
bigyoung11 分钟前
babel 自定义plugin中,如何判断一个ast中是否是jsx文件
前端·javascript·babel
指尖的记忆41 分钟前
当代前端人的 “生存技能树”:从切图仔到全栈侠的魔幻升级
前端·程序员
草履虫建模1 小时前
Ajax原理、用法与经典代码实例
java·前端·javascript·ajax·intellij-idea
轻语呢喃1 小时前
useReducer : hook 中的响应式状态管理
javascript·后端·react.js
时寒的笔记1 小时前
js入门01
开发语言·前端·javascript
陈随易1 小时前
MoonBit能给前端开发带来什么好处和实际案例演示
前端·后端·程序员
996幸存者1 小时前
uniapp图片上传组件封装,支持添加、压缩、上传(同时上传、顺序上传)、预览、删除
前端