你好,雨乐!
昨晚(2022年5月10日)19:33,我突然收到了一条文章评论。
当时我心里一惊,怀疑是我上篇文章中的代码在从编辑器复制到文章时出现了错误。然而,我觉得这不太可能,因为每篇文章的代码都是在机器上运行验证后才发布的,所以出错的概率应该很低。
但既然有读者提出了问题,我必须认真验证一番。遗憾的是当时我在地铁上,无法使用电脑进行实操验证,于是我使用了一个在线编辑器,编写了一个简单的代码来进行测试:
测试结果显示没有问题。尽管如此,既然有读者反馈说在使用中存在问题,这应该不是无中生有,于是我找到了几位朋友在线帮忙查看。
这个:
这个:
还有这个:
从大家的反馈来看,仍然无法确定问题所在,大家都只是觉得可能有问题。
终于回到家后,趁着孩子在玩,我赶紧拿出电脑,把文章中的代码重新复制、编译、运行,一切正常。这就奇怪了,难道路径不同导致结果不同吗?
环境不同?我突然想起群里一位同学之前私聊过我,说他运行文章中的代码时会崩溃。
因为之前忙于工作,我没有仔细了解。在咨询了这位同学后,他回复说是在Windows下编译运行的。
由于本地没有Windows环境,我找人帮忙在VS上执行了代码,结果如下:
多奥淘宝客程序免费版拥有淘宝客站点的基本功能,手动更新少,管理简单等优点,适合刚接触网站的淘客们,或者是兼职做淘客们。同样拥有VIP版的模板引擎技 术、强大的文件缓存机制,但没有VIP版的伪原创跟自定义URL等多项创新的搜索引擎优化技术,除此之外也是一款高效的API数据系统实现无人值守全自动 化运行的淘宝客网站程序。4月3日淘宝联盟重新开放淘宝API申请,新用户也可使用了
在Windows上出现了错误,看来该读者是在Windows环境下执行这段代码的。
既然在Windows和Linux上的运行结果不同,有必要深入研究源码实现。
MSVC实现了vector
代码语言:javascript代码运行次数:0运行复制```javascript _CONSTEXPR20 void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) { // ... _Reallocate_exactly(_Newcapacity); // ... }
从上述代码来看,我们只需要关注_Reallocate_exactly的实现即可,于是继续查看该函数的实现,如下:代码语言:javascript代码运行次数:0运行复制
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_castzuojiankuohaophpcnsize_typeyoujiankuohaophpcn(_Mylast - _Myfirst); const pointer _Newvec = _Al.allocate(_Newcapacity); // ... }zuojiankuohaophpcn/size_typeyoujiankuohaophpcn
我们注意到vector_Myfirst == _Mylast),然后调用allocator.allocate()函数分配了内存,那么继续看vector::operator[]的实现,如下:
代码语言:javascript代码运行次数:0运行复制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 (_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range");#endif // _CONTAINER_DEBUG_LEVEL > 0 return _My_data._Myfirst[_Pos]; }
我们注意到上面一句代码_Pos zuojiankuohaophpcn (_My_data._Mylast - _My_data._Myfirst),在前面内容中,有提到_Myfirst == _Mylast,因此该处代码不成立,所以引发了崩溃,这就是Windows下运行结果出错的原因。GNU实现gnu下vector::reserve()实现如下:
代码语言:javascript代码运行次数:0运行复制javascript templatezuojiankuohaophpcntypename _alloc="" _tp="" typename=""youjiankuohaophpcn void vectorzuojiankuohaophpcn_tp _alloc=""youjiankuohaophpcn:: reserve(size_type __n) { if (__n youjiankuohaophpcn this-youjiankuohaophpcnmax_size()) __throw_length_error(__N("vector::reserve")); if (this-youjiankuohaophpcncapacity() zuojiankuohaophpcn __n) { const size_type __old_size = size(); pointer __tmp = _M_allocate(__n); this-youjiankuohaophpcn_M_impl._M_finish = _GLIBCXX_MAKE_MOVE_ITERATOR(this-youjiankuohaophpcn_M_impl._M_start), _GLIBCXX_MAKE_MOVE_ITERATOR(this-youjiankuohaophpcn_M_impl._M_finish)); std::_Destroy(this-youjiankuohaophpcn_M_impl._M_start, this-youjiankuohaophpcn_M_impl._M_finish, _M_get_Tp_allocator()); _M_deallocate(this-youjiankuohaophpcn_M_impl._M_start, this-youjiankuohaophpcn_M_impl._M_end_of_storage - this-youjiankuohaophpcn_M_impl._M_start); this-youjiankuohaophpcn_M_impl._M_start = __tmp; this-youjiankuohaophpcn_M_impl._M_finish = __tmp + __old_size; this-youjiankuohaophpcn_M_impl._M_end_of_storage = this-youjiankuohaophpcn_M_impl._M_start + __n; } } }zuojiankuohaophpcn/_tpyoujiankuohaophpcnzuojiankuohaophpcn/typenameyoujiankuohaophpcn
从上述实现可以看出,reserve()函数分配了所需的内存空间,并且与MSVC一样的是经过reserve之后_M_start == _M_finish,这是因为__old_size为0。
我们继续查看vector::operator[]的实现,如下所示:
代码语言:javascript代码运行次数:0运行复制javascript const_reference operator[](size_type n) const { return *(this->_M_impl._M_start + n); }
与MSVC的operator实现不同的是,gnu的实现中,直接通过首地址+n来进行元素访问(而没有进行size判断),因为内存已经完成分配,所以这样操作是没问题的,这就是Linux环境下执行没问题的原因。
结语本想与该读者继续私聊,无奈已经被取消关注?。
昨晚,我也对读者提出的问题进行了反思。虽然代码在Linux环境下运行没有问题,但仍然会有一部分Windows的读者认为代码有误。因此,在未来的文章中,如果涉及到代码,我会尽量考虑编译环境的因素。
今天的文章就到这里,我们下期见!









