一次C++调试记录


之前开发用Linux C比较多,C++中的STL 容器基本没有接触过。最近在学习C++,平时用到c++ 17中的部分新特性,下面就简单分享下自己C++的学习流程。 一、环境搭建 本人使用的是CentOS 7系统,该系统默认的g++版本不支持c++17的新特性。所以,首先需要做的就是升级新版本的g++。
  1. ftp://ftp.mirrorservice.org/sites/sourceware.org/pub/gcc/releases/网站上选择支持c++17的gcc版本,并使用wget下载到Linux系统中:wget ftp://ftp.mirrorservice.org/sites/sourceware.org/pub/gcc/releases/gcc-7.1.0/gcc-7.1.0.tar.bz2
  2. 安装编译gcc需要的依赖包 sudo yuminstall gmp-devel mpfr-devel libmpc-devel -y
  3. 解压gcc压缩包到temp文件夹 tar -jxf gcc-7.1.0.tar.bz2 -C temp
  4. 进入到temp/gcc目录下,执行 gcc./configure --enable-checking=release --enable-languages=c,c++ --disable-multilib && make 进行gcc的编译(这个步骤耗时较长)
  5. 安装新版gcc sudo make install
  6. 由于在./configure阶段未指定安装路径,那么新版的gcc的默认安装位置就是/usr/local/目录下,修改标准库的软连接使其指向新版本的标准库sudo ln -sf /usr/local/lib64/libstdc++.so.6.0.23 /lib64/libstdc++.so.6
  7. 需要使用c++17的特性时,需要在Makefile的CXXFLAGS变量中添加 -std=c++17
gdb默认情况下是不支持c++容器输出的,不过在gdb 7.0版本之后,可以通过添加插件的方式来支持c++容器输出
  1. 检查gdb版本 gdb --version, (如果版本号低于7.0就不用往下看了)
  2. 在当前用户的home目录中(如/home/sxhlinux)下载 插件代码svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python(没有svn的,需要通过 sudo yum install svn -y 安装)然后执行mv python .gdb_stl 将该文件夹重命名(使其隐藏)
  3. 执行 vim ~/.gdbinit,编辑gdb配置文件,添加如下内容
    add-auto-load-safe-path /usr/local/lib64/libstdc++.so.6.0.23-gdb.py  #文件的版本号,根据这个目录中的实际文件版本号确定
     
    python
    import sys
    sys.path.append("/usr/local/share/gcc-7.1.0/python")
    sys.path.insert(0, '/home/sxhlinux/.gdb_stl')     #注:将第二个参数中的路径改成自己的.gdb_stl文件夹路径
    from libstdcxx.v6.printers import register_libstdcxx_printers
    register_libstdcxx_printers (None)
    end
    
二、gdb 调试示例
  1. 下面的代码是将带有数字特征的分词(用unorder_map保存),按照一定的规则(分词的数字特征)进行合并
    #include <iostream>
    #include <cstdlib>
    #include <map>
    #include <unordered_map>
     
    using namespace std;
     
    template <typename T1, typename T2>
    bool merge_tokens(T1 &target, const T2 &rules)
    {
        auto pre = target.begin();
        for (auto token = target.begin(); token != target.end(); ) {
            if (pre == token) {
                token ++;
                continue;
            }
     
            auto range = rules.equal_range(pre->second);
            auto it = range.first;
            for (; it != range.second; it++) {
                if (it->second == token->second) {
                    break;
                }
            }
     
            if (it == range.second) {
                pre = token;
                token ++;
            }
            else {
                pre->first += token->first;
    //          target.insert(std::make_pair<typename T1::key_type, typename T1::mapped_type>(pre->first + token->first, 16));
                pre->second = 16;
                token = target.erase(token);
                pre = token;
            }
        }
    }
     
    int main ( int argc, char *argv[] )
    {
        unordered_map<string, size_t> tokens = {{"def", 22}, {"ghi", 100}, {"abc", 22}};
        unordered_multimap<size_t, size_t> rules = {{22, 100}, {100, 22}, {1, 38}};
        merge_tokens(tokens, rules);
     
        return EXIT_SUCCESS;
    }               /* ----------  end of function main  ---------- */
    
  1. 编译该文件,提示 31行
    test.cpp:31:15: error: passing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers [-fpermissive] 
    pre->first += token->first;  
    ~~~~~~~~~~~^~~~~~~~~~~~~~
     
    /usr/local/include/c++/7.1.0/bits/basic_string.h:1122:7: note:   in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator+=(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
           operator+=(const basic_string& __str)
           ^~~~~~~~
    
根据错误提示:string 的运算符 += 要求参数是一个 const string类型(作为右值,非const类型也可以作为const类型的参数使用),返回值是一个string类型。再看 出错的语句pre->first += token->first; 根据mian函数中的tokens的定义,token和pre的first成员都应该是string而不是const string。
  1. 将报错的这一行注释掉,然后用gdb查看下pre->first和token->first的具体类型。具体如下
    (gdb) whatis target
    type = std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long> &
    (gdb) whatis target.begin()
    type = std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unsigned long> > >::iterator
    (gdb) whatis pre
    type = std::__detail::_Node_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unsigned long>, false, true>
    
根据上面显示的,target两个参数类型确实和定义的一样,string和unsigned long;target.begin()类型说明中的std::allocator的模板参数pair的第一个参数为 string const,说明在创建unordered_map时,key的类型为const string而不是string(猜测这跟map与key相关的只有增加、删除而没有修改操作有关)。因为allocator在申请空间时,已经隐式的将string转成了const string,所以,pre->first的类型是const string而不是string(也就无法进行+=,=等相关操作)。
  1. 根据第三步的分析结果,要实现合并元素的效果,只能是将合并后的值作为一个新的pair插入到原来的map中,然后将原来的两个pair删除。代码如下:
                target.insert(std::make_pair<typename T1::key_type, typename T1::mapped_type>(pre->first + token->first, 16));
                target.erase(pre);
                token = target.erase(token);
                pre = token;
    
三、总结 很多时候我们遇到问题首先想到的是将错误复制下来,然后粘贴到google搜索框中,漫无目的的去寻找答案,而不是仔细分析查看gcc给出的错误提示。跟我的经验,很多时候gcc给出的提示相当明显,认真仔细阅读大部分可以很快找出解决方案,剩余的一部分棘手问题可以借助搜索引擎(PS:当搜索英文提示时,如果没有google,可以使用英文版的必应,效果也不错) 优质内容筛选与推荐>>
1、程序员的十大经验和教训
2、MySQL(关系型数据库管理系统)
3、ActiveMq
4、编码及连接
5、css 圆角按钮


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号