前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术文被diss了

技术文被diss了

作者头像
高性能架构探索
发布2022-08-25 16:22:16
3830
发布2022-08-25 16:22:16
举报
文章被收录于专栏:技术随笔心得

你好,我是雨乐!

昨天(2022.05.10)晚上19:33分的时候,突然收到一条文章回复。

当时心里突然一慌,难道我上篇文章中的代码,从编辑器中粘贴的时候出错了?第一感觉是不可能,因为每篇文章的代码都会在机器上运行之后,才会放到文章中,所以,感觉出问题的概率不大呀!

不过,既然有读者提出来了,我也得严格验证下,奈何当时在地铁上,没法在电脑上实操验证,所以找了个在线编辑器,写了个简单代码进行验证:

发现没问题,既然有读者反馈这样使用有问题,那么应该不会无缘无故评论的,所以当时在线找了几个人帮忙一起看了下。

这个:

这个:

还有这个:

从大家的反馈来看,还是不确定,都只是感觉有问题。

终于到家了,趁着娃在玩,赶紧拿出电脑,把文章中的代码cv了一下,然后编译、运行,一切正常,这就奇怪了,难道是环境不同导致的结果不同?

环境不同?

突然想起群里某个同学之前也跟我私聊过,说是文章中的代码他运行后会崩溃。

因为之前在忙工作的事,所以也没有仔细去了解,在咨询了该同学后,其回复是在Windows下执行编译的。

因为本地没有Windows环境,所以找人帮忙在VS上执行了下代码,结果如下:

Windows上报错,那么看来该读者就是Windows环境上执行该段代码了。

深入源码

既然在Windows上和Linux上运行结果不一样,那么就有必要去看看源码实现。

MSVC实现

vector<T>::reserve()实现如下:

代码语言:javascript
复制
 _CONSTEXPR20 void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) {
      // ...  
           _Reallocate_exactly(_Newcapacity);
      // ...
    }

从上述代码来看,我们只需要关心_Reallocate_exactly实现即可,于是继续扒该函数实现,如下:

代码语言:javascript
复制
_CONSTEXPR20 void _Reallocate_exactly(const size_type _Newcapacity) {
        // set capacity to _Newcapacity (without geometric growth), provide strong guarantee
        auto& _Al         = _Getal();
        auto& _My_data    = _Mypair._Myval2;
        pointer& _Myfirst = _My_data._Myfirst;
        pointer& _Mylast  = _My_data._Mylast;

        const auto _Size = static_cast<size_type>(_Mylast - _Myfirst);

        const pointer _Newvec = _Al.allocate(_Newcapacity);

        // ...
    }

我们注意到vector<T>::reserve中获取了_Myfirst和_Mylast(注意,此处_Myfirst == _Mylast),然后调用allocator.allocate()函数分配了内存,那么继续看vector::operator[]实现,如下:

代码语言:javascript
复制
_NODISCARD _CONSTEXPR20 const _Ty& operator[](const size_type _Pos) const noexcept /* strengthened */ {
        auto& _My_data = _Mypair._Myval2;
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(
            _Pos < static_cast<size_type>(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0

        return _My_data._Myfirst[_Pos];
    }

我们注意到上面一句代码_Pos < xxx,在前面内容中,有提到_Myfirst == _Mylast,因此该处代码不成立,所以引发了崩溃,这就是Windows下运行结果出错的原因

GNU实现

gnu下vector<T>::reserve()实现如下:

代码语言:javascript
复制
template<typename _Tp, typename _Alloc>
     void
     vector<_Tp, _Alloc>::
     reserve(size_type __n)
     {
       if (__n > this->max_size())
     __throw_length_error(__N("vector::reserve"));
       if (this->capacity() < __n)
     {
       const size_type __old_size = size();
       pointer __tmp = _M_allocate_and_copy(__n,
          _GLIBCXX_MAKE_MOVE_ITERATOR(this->_M_impl._M_start),
          _GLIBCXX_MAKE_MOVE_ITERATOR(this->_M_impl._M_finish));
       std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
             _M_get_Tp_allocator());
       _M_deallocate(this->_M_impl._M_start,
             this->_M_impl._M_end_of_storage
             - this->_M_impl._M_start);
       this->_M_impl._M_start = __tmp;
       this->_M_impl._M_finish = __tmp + __old_size;
       this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
     }
     }

从上述实现可以看出,reserve()函数分配了所需要的内存空间,而且与MSVC一样的是经过reserve之后_M_start == _M_finish,这是因为__old_size为0。

好了,我们继续看下vector::operator[]的实现,如下所示:

代码语言:javascript
复制
const_reference
       operator[](size_type __n) const
       { return *(this->_M_impl._M_start + __n); }

与MSVC的operator实现不同的是,gnu的实现中,直接通过首地址+n来进行元素访问(而没有进行size判断),因为内存已经完成分配,所以这样操作是没问题的,这就是Linux环境下执行没问题的原因

结语

本想跟该读者继续私聊,无奈已经被取关😃。

昨天晚上,我也对读者提出的问题进行了反思,虽然代码在Linux环境上执行没问题,但是仍然会有一部分Windows的读者会认为代码是错的。所以后面的写文过程中,如果涉及到代码,我会尽量考虑编译环境的因素。

好了,今天的文章就到这,我们下期见!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能架构探索 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境不同?
  • 深入源码
    • MSVC实现
      • GNU实现
      • 结语
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档