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

前端实战:手把手教你实现浏览器通知功能

前端实战:手把手教你实现浏览器通知功能

前端入门:浏览器通知功能从0到1实现指南 作为前端学习者,你可能见过这样的场景:打开网页版聊天工具,就算把浏览器最小化,桌面也会弹出“新消息”提醒;或者某些网站的活动通知,会直接显示在电脑/手机桌面上。这种功能就是「浏览器桌面通知」,今天我们就从零开始,搞懂它、学会用它。 一、先搞懂3个基础问题 1. 什么是浏览器桌面通知? 简单说,就是网页能在浏览器窗口外面(比如电脑桌面、手机屏幕)给你发提醒。哪怕浏览器最小化、甚至页面切到后台,只要权限允许,都能收到通知,不用一直盯着网页。 2. 什么时候会用到它? 常见场景很贴近日常: * 网页版微信/QQ的新消息提醒; * 工作系统的审批提醒、任务到期通知; * 电商网站的订单状态更新(比如“你的快递已发货”); * 新闻/小说网站的订阅内容更新提醒。 3. 用起来难吗?有什么限制? 不难!核心就2步:先让用户同意开启通知(申请权限)

By Ne0inhk
【最新版】防伪溯源一体化管理系统+uniapp前端+搭建教程

【最新版】防伪溯源一体化管理系统+uniapp前端+搭建教程

一.介绍 防伪溯源一体化管理系统基于ThinkPHP和Uniapp进行开发的多平台(微信小程序、H5网页)溯源、防伪、管理一体化独立系统,拥有强大的防伪码和溯源码双码生成功能(内置多种生成规则)、批量大量导出防伪和溯源码码数据、支持代理商管理端(团队管理、采购,邀请代理商、出库等功能)、支持招商经理管理端(可管理代理商团队,邀请代理商,数据统计,采购订单统计),支持出厂员端(出库、入库)、文章资讯、自定义展示查询页显示数据、查询记录、溯源记录追踪等功能。前后端无加密源代码和数据库,独立部署。 二.搭建环境 系统环境:CentOS、 运行环境:宝 塔 Linux 网站环境:Nginx 1.2.22 + MySQL 5.6 + PHP-7.4 常见插件:fileinfo

By Ne0inhk
全Web化智慧PACS/RIS系统源码 (纯B/S架构)

全Web化智慧PACS/RIS系统源码 (纯B/S架构)

告别传统C/S架构的笨重客户端!本套源码采用纯Web前端技术实现极速调阅,支持CT、核磁(MR)、DR、超声等多模态影像。内置专业级Web Viewer,支持MPR多平面重建、MIP、VR体渲染。自带RIS全流程管理。100%无加密源码交付,是医疗软件公司打造云PACS、区域影像中心的核心利器! 一、 为什么医疗企业都在寻找真正的WebPACS? 传统的PACS系统多采用C++或C#开发,需要医生在电脑上一台台安装庞大的客户端,维护成本极高,且无法适应如今“互联网医院”和“医共体远程诊断”的需求。 * 极速跨平台: 本系统基于HTML5+WebGL技术,医生只需打开浏览器,即可实现秒级加载百兆级影像,支持Windows、Mac甚至iPad移动阅片。 * 省去百万研发费: 医疗影像的底层解析(如窗宽窗位调节、各种DICOM Tag解析、图像无损压缩算法)是深水区,直接购买本源码,省去2-3年以上的底层图形学研发周期。 * 高价值变现: 本源码不仅可独立作为医院影像科管理系统出售,更可作为“影像插件”

By Ne0inhk

voidImageViewer:终极轻量级图像查看器,完美支持GIF/WEBP动画播放

voidImageViewer:终极轻量级图像查看器,完美支持GIF/WEBP动画播放 【免费下载链接】voidImageViewerImage Viewer for Windows with GIF support 项目地址: https://gitcode.com/gh_mirrors/vo/voidImageViewer voidImageViewer 是一款专为 Windows 平台设计的轻量级图像查看器,以其极速加载和流畅的动画播放工具功能而备受好评。这款工具不仅体积小巧,还能高效处理多种主流图像格式,为用户带来前所未有的图片浏览体验。 🚀 项目亮点:为什么选择voidImageViewer? 极速启动与运行:voidImageViewer 的启动速度令人惊叹,几乎在点击瞬间即可完成加载,大幅提升了工作效率。 资源占用极低:作为真正的轻量级应用,voidImageViewer 在后台运行时几乎不占用系统资源,确保您在进行其他工作时依然保持系统流畅。 跨格式兼容性:完美支持 BMP、GIF、ICO、JPG、TIF 和 WEBP 等多种图像格式,

By Ne0inhk