欢迎光临散文网 会员登陆 & 注册

C++对象指针无法进行-n或+n [包含VS Code启动调试教程]

2022-11-29 23:43 作者:凫水亿  | 我要投稿

Gcc版本:gcc (Rev3, Built by MSYS2 project) 12.1.0

VS Code版本:

                        版本: 1.70.1 (system setup)

                        提交: 6d9b74a70ca9c7733b29f0456fd8195364076dda

                        日期: 2022-08-10T06:08:33.642Z

                        Electron: 18.3.5

                        Chromium: 100.0.4896.160

                        Node.js: 16.13.2

                        V8: 10.0.139.17-electron.0

                        OS: Windows_NT x64 10.0.22000


最近在学 C++,发现对象数组指针无法进行 + n 或 -n操作,如果进行 +n或者 -n操作,那么输出就会异常。

先说解决办法:使用++,--代替 +n 或 -n 操作。

Bug复现

1.定义一个类

2.在堆上分配对象数组

3.给三个对象的成员变量赋值

4.打算用 for 循环输出,所以直接对指针 -2,将指针复原

5.使用 for 输出

很显然,没有得到我们想要的结果。这是为什么呢?

调试

通过观察,发现输出的 "葫芦娃" 是最后一个初始化的对象成员变量,有没有一种可能——指针没有向后移动两个对象长度。

1.配置默认生成任务[单个源文件]

说干就干,我们使用 VS Code 调试本程序。依次点击 终端>配置默认生成任务 ,在下拉框选择 C/C++:g++.exe 生成活动文件

系统会帮我们自动生成默认配置:

并且在资源管理器的工作空间下自动生成 tasks.json 文件

多出来的是之前已经配置好了

2.配置调试文件

点击侧边栏的运行和调试按钮,然后点击 创建launch.json 文件

在下拉栏中选择 C++(GDB/LLDB)

点击右下角的添加配置

在弹出的下拉栏中选择 C/C++: (gdb) 启动 选项

系统会自动生成调试器配置文件模板,我们需要修改几个东西。

第一,在 "program":后输入字符串 "${fileDirname}\\${fileBasenameNoExtension}.exe" ,${fileDirname} 表示当前激活文件的目录名称,\\ 在windows平台代表 / 文件夹分隔符,${fileBasenameNoExtension} 表示当前激活文件的名字,不包括扩展名。

第二,在 "miDebuggerPath": 后输入你 gdb.exe 所在的完整目录,这是你调试器的路径。举例:我的目录如下

所以填写:"miDebuggerPath": "D:\\msys64\\mingw64\\bin\\gdb.exe" ,你也可以把 \\ 换成 /  "miDebuggerPath": "D:/msys64/mingw64/bin/gdb.exe" 都是可以的。

第三,在这个花括号内,添加最后一个选项 "preLaunchTask": 这个选项表示:在启动调试之前,先启动一个任务;由于我们选择的是启动调试,所以源文件需要提前编译好,再将编译好的文件进行启动调试,而这个提前的任务就是我们刚才设置的默认生成任务。

现在,打开 tasks.json 文件找到 "label",复制后面的字符串到 "preLaunchTask":后面。



3.调试

在开始调试之前,先回到我们的源文件,还有几件事没做。

1.更改字符集;调试器的默认编码是 utf-8 , 如果以现在的字符集去生成可执行文件,在调试输出时将产生乱码。点击右下角状态的字符集,我的默认字符集是 GB 2312 ,现在点击它

更改字符集

选择通过编码保存

在弹出的字符集中选择 utf-8


选择utf-8字符集

2.设置断点。只有设置断点,调试器才会根据断点停止,否则直接完成运行程序,那调试也无从说起了。在 VS Code中设置断点和 VS 中是一样的:在行号的左边,鼠标左键单击,出现红色圆点,就代表断点已经设置完成。

3.开始调试。点击左边栏的 启动和调试 按钮,现在可以看到我们配置的启动调试了。点击小三角形,开始调试。它将依次生成可执行文件.exe,然后执行启动调试。注意:必须在被激活的文件上执行调试(也就是你想要调试的文件上)。

此时程序停在了断点处:

调试面板位于屏幕上方中间处:

调试面板

~@  点击最左边的六个小点可以拖动调试面板

~@  第一个一个竖线挨着一个三角形的按钮,表示继续执行程序按钮,它的作用是执行到下一个断点,再停止程序。

~@  第二个按钮,像跳过一个点的按钮,表示跳过函数内部执行过程,如跳过 Aclass[0].say(); 函数,直接运行到第43 行。

~@  第三个按钮,一个竖向的箭头指向一个点,表示单步执行,它的作用是,以精确的程序执行步骤运行程序,在调试过程中遇到函数,则会进入那个函数内部,一步一步执行每一行代码,只有执行完那个函数内部的代码才会回到上一层函数的栈上来。

~@  第四个按钮,一个点位于一个竖向向上箭头的底部。表示单步跳出,能够从函数内部直接跳到上一层函数栈上来。

~@  第五个按钮,一个带箭头的圈圈,表示重启调试过程。

~@  第六个按钮,一个正方形图像,表示停止调试。


4.添加监视。在左边栏中可以看到本地变量的名称和地址:

右键点击我们要监视的变量 Aclass,在弹出的菜单中选择 添加到监视

在变量窗口下,就是监视窗口。在 Aclass的左边有一个 > 按钮,点击它,可以展开详细的信息,比如该变量的成员信息,地址信息:

回到正题,我们要验证——使用地址 -n改变不了当前地址。点击单步执行,监视窗口刷新了变量信息,右键单击变量名,点击复制值

1)  我们记录第一次变量的地址信息:

2)  运行到 变量 ++ 之后,记录第二次变量地址的信息。

改变变量的地址

地址:

3)  记录最后一次 变量 ++ 地址改变信息:

不难发现三个地址相差了 0x 10

4)  记录 变量 -2 时地址改变的信息:

细心的朋友已经发现了,调试器直接跳过了Aclass-2这行代码

地址:

通过对比,我们发现运行 Aclass-2并不会改变变量的地址,问题出在这。

我们将Aclass-2; 这行代码换成两行 Aclass--;然后再调试一次,发现地址变化出现了预期效果。

结果告诉我们,在现版本的C++中已经不支持使用 -n、+n这种方法执行地址操作了。使用++,--依旧有效。

源码展示:


文章存在歧义是必然的,此时应请提供:gcc版本号,VS code版本号,供大家斟酌仔细分析。


C++对象指针无法进行-n或+n [包含VS Code启动调试教程]的评论 (共 条)

分享到微博请遵守国家法律