C++初学者的学习进程(指针)

对于C++新手来说,指针无疑是入门路上的“拦路虎”。很多人刚接触时会被“*”“&”搞得晕头转向,甚至觉得指针“反人类”。但实际上,指针的核心逻辑非常简单——它就是一个“存储内存地址的变量”。今天这篇文章,我们就从最基础的概念开始,一步步拆解C++指针的基础知识,搭配大量可直接运行的代码示例,让你彻底搞懂指针的本

一、先搞懂核心:指针到底是什么?

在讲解指针之前,我们先回顾一个基础概念:变量在内存中的存储

当我们在C++中定义一个变量(比如int a = 10;)时,计算机会在内存中开辟一块“存储空间”来存放这个变量的值(10)。而这块存储空间有一个唯一的“编号”,这个编号就是内存地址(类似我们的身份证号,唯一标识一块内存区域)。

而指针,本质上就是一个专门用来存储内存地址的变量。普通变量存储的是“数据本身”(比如a存储10),指针变量存储的是“数据所在的内存地址”(比如指针p存储a的地址)。

用一个通俗的比喻理解:

  • 普通变量(a=10):相当于一个“快递盒子”,里面装的是“快递物品”(10);
  • 指针变量(p=&a):相当于一个“纸条”,上面写着“快递盒子”(a)的存放地址,通过这个地址就能找到对应的盒子。

质和使用方法。

二、指针的基础语法:定义、取地址、解引用

指针的核心操作有三个:定义指针变量获取变量的内存地址通过指针访问变量的值。对应的语法符号分别是:*(定义指针、解引用)和&(取地址)。

2.1 语法1:取地址符 &——获取变量的内存地址

取地址符&的作用是“获取变量所在的内存地址”。语法格式:&变量名

示例代码:

#include <iostream>

using namespace std;

int main()

{ int a = 10;

// 定义普通变量a,存储值10

cout << "变量a的值:" << a << endl;

// 输出:10

cout << "变量a的内存地址:" << &a << endl;

// 输出a的内存地址(十六进制,比如0x6ffe14) return 0; }

运行结果说明:不同电脑、不同运行次数,a的内存地址可能不一样(因为内存分配是动态的),但格式都是十六进制(以0x开头),这是内存地址的标准表示方式。

2.2 语法2:定义指针变量——存储内存地址

指针变量的定义格式:数据类型 * 指针变量名;

注意要点:

  • “数据类型”:指针变量存储的是“某个变量的地址”,这个“某个变量”的数据类型就是指针的“基类型”。比如存储int变量的地址,指针类型就是int*;存储char变量的地址,指针类型就是char*。
  • *:表示这是一个指针变量,不能省略(否则就成了普通变量)。

示例代码(定义指向int变量的指针):

#include <iostream> using namespace std;

int main() { int a = 10;

// 普通int变量a int * p;

// 定义int*类型的指针变量p(专门存储int变量的地址) p = &a;

// 把a的内存地址赋值给指针p(p现在存储a的地址)

cout << "指针p存储的地址:" << p << endl;

// 输出:和&a一样的地址(比如0x6ffe14)

cout << "指针p本身的地址:" << &p << endl;

// 输出p自己的内存地址(指针也是变量,也占内存)

return 0;

}

关键提醒:指针变量必须“先定义,再赋值”,且赋值的地址必须和指针的基类型匹配(不能把char变量的地址赋值给int*指针,否则会编译报错或出现未知错误)。

2.3 语法3:解引用符 *——通过指针访问变量的值

我们定义指针、存储地址的最终目的,是为了通过地址找到对应的变量,进而操作它的值。这时候就需要用到“解引用符”*(和定义指针时的*是同一个符号,但作用不同)。

解引用的语法:*指针变量名,表示“通过指针变量存储的地址,找到对应的变量”,等价于“指针指向的变量”。

示例代码:

#include <iostream>

using namespace std;

int main() { int a = 10; int * p = &a;

// 定义指针p并直接赋值(推荐写法,简洁)

// 通过指针访问变量a的值

cout << "通过指针p访问a的值:" << *p << endl;

// 输出:10(等价于cout << a << endl;)

// 通过指针修改变量a的值 *p = 20;

// 等价于 a = 20;

cout << "修改后a的值:" << a << endl;

// 输出:20

return 0; }

这里一定要区分清楚*的两个作用:

  • 定义指针时:int * p; 中的*表示“p是指针变量”;
  • 使用指针时:*p = 20; 中的*表示“解引用”,即“通过p的地址找到对应的变量”。

三、指针的重要特性:类型匹配

前面我们提到过,指针的“基类型”必须和它指向的变量类型一致,否则会出现错误。这是新手最容易踩的坑之一,我们用代码示例说明:

#include <iostream>

using namespace std;

int main()

{ int a = 10; char b = 'A'; int * p1 = &a;

// 正确:int*指针指向int变量

// int * p2 = &b;

// 错误:int*指针不能指向char变量(类型不匹配)

char * p3 = &b;

// 正确:char*指针指向char变量

return 0; }

为什么必须类型匹配?因为不同类型的变量占用的内存空间大小不同(比如int占4字节,char占1字节),指针解引用时需要知道“读取多少字节的内存”——int*指针解引用时读取4字节,char*指针读取1字节。如果类型不匹配,就会读取错误的内存数据,导致程序异常。

四、空指针与野指针:新手必须避开的坑

指针变量如果未被正确赋值,就可能成为“野指针”,这是程序崩溃的常见原因之一。另外,还有一种“空指针”,是C++中专门用来表示“指针未指向任何有效内存”的特殊值。

4.1 野指针(危险!)

野指针:指针变量未被初始化,或者指向的内存地址已经被释放(无效地址)。访问野指针会导致程序崩溃(段错误)。

错误示例(野指针):

#include <iostream>

using namespace std;

int main()

{ int * p;

// 未初始化的指针,存储的是随机的垃圾地址(野指针)

// *p = 10;

// 错误:访问野指针,程序会崩溃

return 0;

}

规避方法:定义指针时,要么直接赋值(指向有效变量),要么先赋值为nullptr(空指针)。

4.2 空指针(安全的“无指向”状态)

空指针是C++11引入的关键字nullptr,它表示“指针未指向任何有效内存地址”。空指针的核心作用是“避免野指针”——当我们暂时不知道指针该指向哪里时,就把它初始化为nullptr

注意:不能解引用空指针(因为它指向无效内存),所以使用前需要判断指针是否为空。

正确示例(空指针的使用):

#include <iostream> 

using namespace std;

int main()

{ int * p = nullptr;

/ 初始化指针为nullptr(空指针)

int a = 10;

// 使用指针前先判断是否为空

if (p == nullptr)

{ p = &a; // 为空时,赋值为有效地址 } *p = 20;

// 现在可以安全使用了

cout << a << endl;

// 输出:20 return 0; }

补充:C++98中曾用NULL表示空指针(本质是宏定义,值为0),但NULL可能会和整数0混淆,因此C++11推荐使用nullptr(专门表示空指针,类型更明确)。

五、指针与数组:天生一对

在C++中,数组名和指针有着非常紧密的联系——数组名本质上是一个“指向数组首元素的常量指针”(不能修改数组名的值,即不能让数组名指向其他地址)。

用代码理解:

#include <iostream>

using namespace std;

int main() { int arr[5] = {1, 2, 3, 4, 5};

// 定义数组arr

// 数组名arr是指向首元素arr[0]的常量指针

cout << "数组名arr的值(首元素地址):" << arr << endl;

cout << "arr[0]的地址:" << &arr[0] << endl;

// 和arr的值完全相同

// 通过指针访问数组元素(两种等价写法)

int * p = arr;

// 指针p指向数组首元素(无需&,因为arr本身就是地址)

cout << "p指向的元素(arr[0]):" << *p << endl;

// 输出:1 cout << "p+1指向的元素(arr[1]):" << *(p+1) << endl;

// 输出:2 cout << "p+2指向的元素(arr[2]):" << *(p+2) << endl; // 输出:3 // 遍历数组(指针方式)

for (int i = 0; i < 5; i++)

{ cout << *(p + i) << " "; // 等价于 arr[i] 或 p[i] }

return 0;

}

核心结论:

  • arr[i] 等价于 *(arr + i)(数组名是常量指针);
  • p[i] 等价于 *(p + i)(指针变量可以像数组一样使用);
  • 指针p+1 表示“指向当前元素的下一个元素”,偏移的字节数由指针类型决定(int*指针偏移4字节,char*指针偏移1字节)。

六、指针的基础应用场景:函数传参(地址传递)

我们知道,C++函数的参数传递默认是“值传递”——函数内部修改参数值,不会影响函数外部的变量(因为函数会拷贝一份参数的副本)。但如果我们想通过函数修改外部变量的值,就可以使用“地址传递”(用指针作为参数)。

示例代码(用指针修改外部变量):

#include <iostream>

using namespace std;

// 函数参数是int*指针,接收外部变量的地址

void changeValue(int * p)

{ *p = 100;

// 解引用指针,修改外部变量的值

}

int main()

{ int a = 10;

cout << "修改前a的值:" << a << endl;

// 输出:10

// 传递a的地址给函数(用&a)

changeValue(&a);

cout << "修改后a的值:" << a << endl;

// 输出:100(外部变量被修改了)

return 0; }

原理:函数接收的是外部变量的地址(指针p存储a的地址),通过解引用*p,可以直接操作a所在的内存空间,因此修改的是外部变量本身,而不是副本。这是指针最常用的场景之一。

七、新手必记的指针基础要点

  1. 指针是“存储内存地址的变量”,核心是“地址”,不是“值”;
  2. 定义指针时必须指定基类型(int*、char*等),且基类型要和指向的变量类型匹配;
  3. &是“取地址符”,*是“定义指针”或“解引用”,注意区分两个作用;
  4. 指针必须初始化(要么指向有效变量,要么赋值为nullptr),避免野指针;
  5. 数组名是“指向首元素的常量指针”,可以直接赋值给同类型指针;
  6. 通过指针传参,可以实现函数对外部变量的修改(地址传递)。

Read more

【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

🔥个人主页: 中草药  🔥专栏:【Java】登神长阶 史诗般的Java成神之路 本文不做Python以及Pycharm安装的详细教程,请大家自行查阅资料,或到官网去下载         Python作为一门 “优雅且强大” 的编程语言,Python 凭借易上手、用途广的特点,成为很多人入门编程的首选。无论是数据分析、人工智能,还是 Web 开发、自动化脚本,Python 都能胜任。但想要用好 Python,扎实的基础语法是关键 —— 本文将结合系统的语法知识,从 “计算器” 级别的简单运算,到数据持久化的文件操作,带你一站式吃透 Python 核心语法,让你看完就能上手写代码! 变量与数据类型:程序的"原材料仓库"         变量就像快递盒,用来装不同类型的数据;数据类型则是快递盒上的标签,告诉我们里面装的是文件、水果还是电子产品。类型系统其实是在对变量进行 "归类"

By Ne0inhk

用 Python 30 分钟做出自己的记事本

🌟 《零基础手把手:用 Python 30 分钟做出自己的记事本》 —— 不是照抄代码,而是理解每行代码的「灵魂」 🧩 第一步:为什么我们需要「基础窗口」?(新手必懂!) ❌ 常见错误:直接写 window.show() 但窗口不显示? ✅ 正确逻辑:程序运行流程图 启动程序 创建应用对象 创建窗口 显示窗口 进入事件循环 📝 代码详解(逐行解释): import sys # 必须!用于接收系统参数(比如文件路径)from PyQt6.QtWidgets import QApplication, QMainWindow # 从PyQt库导入两个核心组件# 1️⃣ 创建应用对象(灵魂!所有PyQt程序必须有) app = QApplication(sys.argv)# sys.argv = 系统传递的命令行参数(比如打开的文件名)

By Ne0inhk
Python+Agent入门实战:0基础搭建可复用AI智能体

Python+Agent入门实战:0基础搭建可复用AI智能体

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 文章目录: * 【前言】 * 一、先理清:Python+Agent,到底强在哪里? * 1.1 核心区别:Python脚本 vs Python+Agent * 1.2 2026年Python+Agent的3个热门入门场景 * 1.3 新手入门核心技术栈 * 二、环境搭建:10分钟搞定Python+Agent开发环境 * 2.1 第一步:安装Python * 2.2 第二步:创建虚拟环境 * 2.3 第三步:安装核心依赖包 * 2.4 第四步:配置OpenAI

By Ne0inhk
【C++】——红黑树的平衡之道:深入实现与优化

【C++】——红黑树的平衡之道:深入实现与优化

坎坷之路,终抵星空。 —— 哈珀·李 《杀死一只反舌鸟》 目录 1. 解密红黑树:平衡与效率的双重奏 2. 搭建红黑树:从零到自平衡的实现之路 2.1 树基打底:设计与框架构建 2.2 插入有道:插入操作的技巧与挑战 2.3 旋转为王:平衡的秘密武器 2.4 查找制胜:高效查询之道 3. 性能透析:红黑树的效率与边界 1. 解密红黑树:平衡与效率的双重奏 * 探讨红黑树如何通过一组简单的规则保持平衡,并提供高效的查询和更新操作。 红黑树是一种特殊的二叉树,它遵循一套独特的规则: 每个节点要么是红色,要么是黑色。根节点必须是黑色的。如果一个节点是红色的,则它的两个子节点必须是黑色的。对于任意一个节点,从该节点到其所有后代叶子节点的简单路径上,必须包含相同数目的黑色节点。每个叶子节点都是黑色的。这里的叶子节点指的是为空的节点。 TIP:红黑树的规则并不要求红黑节点严格交替出现。

By Ne0inhk