V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ActualAvocado
V2EX  ›  C++

[C++]为什么 For 循环在调试时执行顺序像个悠悠球?

  •  
  •   ActualAvocado · 2022-03-18 18:41:20 +08:00 · 2957 次点击
    这是一个创建于 980 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一般的 for 执行顺序是 for 循环中最后一条语句执行完回到 for 循环的第一行 https://www.youtube.com/watch?v=EfXHIhP2qU0 但是这个视频中(刚上传的),这个 for 循环最后一句执行后,会反向执行,像个悠悠球一样。

    我用的是 vscode 远程连接到服务器( ubuntu20.04 ) vscode launch.json 调试的工具用的是 gdb 。 源码是 源码目录 下的 evaluate.cpp

    但是我为了方便调试,把第 143 行 #pragma omp parallel for 删除了。

    14 条回复    2022-03-19 15:45:42 +08:00
    BrettD
        1
    BrettD  
       2022-03-18 18:48:04 +08:00 via iPhone
    开了优化?
    ActualAvocado
        2
    ActualAvocado  
    OP
       2022-03-18 18:50:11 +08:00
    @BrettD 是指编译器优化吗?
    Inn0Vat10n
        3
    Inn0Vat10n  
       2022-03-18 19:00:49 +08:00
    反向的过程是在调析构吧
    zzxxisme
        4
    zzxxisme  
       2022-03-18 19:06:16 +08:00 via Android
    我猜反向执行是在执行 string 和 vector 的析构函数。可以试着回去的时候看看能不能 step in ?
    FrankHB
        5
    FrankHB  
       2022-03-18 20:10:01 +08:00
    源代码中的行和生成代码的顺序本来就不保证相同(其实都没法一一对应,因为有的可能根本不生成目标代码)。由于编译器 IL 和目标代码也不保证一一对应,指令重排序在后端也可能发生。优化可能会增加这种机会(因为不优化编译器可能会偷懒就默认源代码翻译顺序了)。默认顺序也不绝对,像 GCC 生成函数实际参数的代码中一般表现出从右到左求值,除非改-feval-order 。
    既然用 gdb ,你 si 一下看看指令走的流程跟你源代码怎么对应的就是了。
    ActualAvocado
        6
    ActualAvocado  
    OP
       2022-03-18 20:36:09 +08:00
    @zzxxisme 哦哦哦 应该是这个原因了,step in 后进析构了
    yulon
        7
    yulon  
       2022-03-18 21:17:45 +08:00
    看完视频大致猜到了是在析构,一看跟帖 v2 聪明人还是很多的,编译器是可以打乱执行顺序,但是那么规律的倒序执行,肯定是有点什么原因在里面的,如果有人觉得大家的问题都不配让你动脑子你其实可以选择不回复。
    LeviMarvin
        8
    LeviMarvin  
       2022-03-18 21:27:25 +08:00
    我用 CLion 的 Debug 给 for 内语句打断点也是,根悠悠球一样,不过记录的信息挺详细的,执行一遍语句后有 ++i 的话 i 也 +1 ,虽然没什么用
    leimao
        10
    leimao  
       2022-03-19 00:34:43 +08:00 via iPhone
    C++不是 scripting language ,你看到的你写的未必就是实际被执行的。
    documentzhangx66
        11
    documentzhangx66  
       2022-03-19 06:49:46 +08:00
    每行前面加个 printf( 自增 ID )试试?这种简单粗暴的办法,让我在不便于 IDE 调试的环境下,解决了不少问题。

    比如:

    源代码:
    int a = 1;
    int b = 2;
    int c + a + b ;


    简单粗暴后:

    printf_s( "V2EX Debug Mode - 位置 " + 1 + 换行 );
    int a = 1;

    printf_s( "V2EX Debug Mode - 位置 " + 2 + 换行 );
    int b = 2;

    printf_s( "V2EX Debug Mode - 位置 " + 3 + 换行 );
    int c + a + b ;

    printf_s( "V2EX Debug Mode - 位置 " + 4 + 换行 );
    jackchenly
        12
    jackchenly  
       2022-03-19 08:54:48 +08:00 via iPad
    调用析构函数
    secondwtq
        13
    secondwtq  
       2022-03-19 11:33:19 +08:00
    建议楼主可以源码和汇编结合看,更方便理解。
    社区上有一些 GDB 的插件,可以做到汇编和源码同屏之类的增强 ncurses UI 。VSCode 好像也有这个功能。或者 godbolt 也可以帮你把汇编和源码对应起来。
    当然我个人体验这些方法的缺点是不 scalable ,源码一多就容易乱,再加上 C++ 要求编译器整很多活,稍微大点的程序就没那么直接。

    我觉得归根结底是现在的 toolchain 还是比较拉,对调试信息的记录和管理简单粗暴,想要干点精细活就很麻烦。
    BIAOXYZ
        14
    BIAOXYZ  
       2022-03-19 15:45:42 +08:00
    以前在 vscode 里单步时也见过,应该只是看起来是“反向执行”,其实根本没执行。你可以按这位老哥 @documentzhangx66 的建议加点打印再试试,应该不会逆序打印出来的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2886 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 02:57 · PVG 10:57 · LAX 18:57 · JFK 21:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.