在csdn回答别人的问题时,偶然间接触到sizeof求sting的内存容量大小的问题,经过测试,结果有些意外,引发自己的深度思考,探索一番做整理。
0:相关知识点
为了分析sizeof(string)的大小,涉及到一些知识点:
====》sizeof()是静态运算符,关注的是参数类型,编译时决定了。
====》类的长度实际是类中非静态成员变量长度,无成员变量是1,有虚函数要维持虚函数表(一个指针大小),有基类时要算基类的成员吧。
====》类模板(通用的类)和模板类(实例化的类)的概念
====》可以用using对复杂的结构重新命名。
====》string的内存分配,实际上还是类中定义一个指针,真正赋值的时候去new具体的大小。
====》typename的新用法:用在模板定义中,把相关名称识别为类型。
1. 问题描述(单纯记录引发我思考的起点):
有新手同学在使用string及数组的时候,有误用,引发我的思考:
//他的本意应该是想定义时确定一个数组的大小,故这样写了代码
string s;
char c[strlen(s.c_str())]={0}; //都是不对的代码
int a[strlen(s.c_str())]={0};
我能明确看出他的不对,因此我期望用demo去给他验证,数组下标是0,这里就涉及字符串string的一个长度问题,我抛出:
//这是在vs 2019上运行的结果:
int main()
{
string s;
//这里我的本意是让他理解及去梳理strlen(),length(),以及sizeof()这几个求长度函数的差异,却让自己深思一下
cout << strlen(s.c_str()) << endl; //0
cout << s.length() << endl; //0
cout << sizeof(s) << endl; //28
cout << sizeof(string) << endl; //28
return 0;
}
//探索后,发现在linux上运行的结果
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
string s;
cout << strlen(s.c_str()) << endl; //0
cout << s.length() << endl; //0
cout << sizeof(s) << endl; //32
cout << sizeof(string) << endl; //32
return 0;
}
检测测试发现:
VS2019上sizeof(string)的长度是28
linux上sizeof(string)的长度是32
VS2022上sizeof(string)的长度是40
2. 为什么sizeof(string)的长度是28,以及32,40
第一眼懵逼,问了一把身边同事,他随口回答是类似array的预分配内存
===>emmmm,有点涉及,但是依然不对,这是string的其他知识点
结合百度,明确以下信息:
====》用sizeof求string的结果,在不同的系统中结果是不同的。
====》用sizeof求string的大小,与string是否初始化也是无关的。
====》sizeof是静态运算符,在编译的时候获取到响应结果,而string对象的申请,一般都是在运行时动态分配。
反思到,sizeof所求,其实是类所占用的大小,可以跟踪代码进行探索。
3:跟踪一下string类的大小
这里我在vs2019上试图跟踪了一下string类。 其他的类似吧~
//string实际是 类模板 传入char
using string = basic_string<char, char_traits<char>, allocator<char>>;
//第一步:
//这里实际是关注basic_string 中传入char时类所占内存的大小。
//实际测试:
cout << sizeof(basic_string111<char, char_traits<char>, allocator<char>>) << endl; //28
//简化basic_string 相关的类中成员,最终类简化后占用内存的成员是:
template <class _Elem, class _Traits = char_traits<_Elem>, class _Alloc = allocator<_Elem>>
class basic_string111 { // null-terminated transparent array of elements
private:
using _Alty = _Rebind_alloc_t<_Alloc, _Elem>;
using _Alty_traits = allocator_traits<_Alty>;
using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
_String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Elem&, const _Elem&>>>;
private:
_Compressed_pair<_Alty, _Scary_val> _Mypair; //这是实际占用的
};
//第二步:
//这里就需要研究成员变量的的大小了: _Compressed_pair<_Alty, _Scary_val> _Mypair;
//实际测试:
cout << sizeof(_Alty) << endl; //1
cout << sizeof(_Scary_val) << endl; //28
cout << sizeof(_Compressed_pair<_Alty, _Scary_val>) << endl; //28
//第三步
//接下来需要研究成员大小: _Compressed_pair<_Alty, _Scary_val>大小为什么是28
//首先获取到 _Compressed_pair 实际也是一个类模板 中间有个成员变量 实际是模板第二个参数
template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first
public:
_Ty2 _Myval2;
using _Mybase = _Ty1; // for visualization
};
//====>所以 _Compressed_pair 的大小,实际上可以确定是第二个模板参数 也就是_Scary_val 的大小。
//====>_Scary_val 实际又是 类模板
using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
_String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Elem&, const _Elem&>>>;
// ====》也就是确定 这个类模板 对应传参实际大小
template <class _Val_types>
class _String_val111 : public _Container_base {
public:
// length of internal buffer, [1, 16]:
static constexpr size_type _BUF_SIZE = 16 / sizeof(char) < 1 ? 1 : 16 / sizeof(char);
union _Bxty { // storage for small buffer or pointer to larger one
_Bxty() noexcept {} // user-provided, for fancy pointers
~_Bxty() noexcept {} // user-provided, for fancy pointers
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr)
} _Bx;
typename size_t _Mysize; // 4个字节 typedef unsigned int size_t;
typename _Val_types::size_type _Myres; // 4个字节 实际是模板参数传参对应的size_type 类型 实际还是size_t
};
//第四步:实际上就是分析_String_val111 所占大小
//为了确定_Val_types::size_type 的类型及长度 跟踪一下模板参数:conditional_t
_String_val111<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<char>,
_String_iter_types<char, typename _Alty_traits::size_type, typename _Alty_traits::difference_type, typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, char&, const char&>>>
//可以确定他实际是 _Simple_types<char>中对应的size_type
template <class _Value_type>
struct _Simple_types1 {
using value_type = _Value_type;
using size_type = size_t; //===》实际是这个 也是unsigned int
using difference_type = ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
};
//分析后可以确定
//_String_val111 类中,成员变量的长度是 union (16) + size_t(4) +_Val_types::size_type(4) +基类成员中有一个指针(4) = 28
最终 跟踪vs2019中string相关类的定义,可以确定sizeof(string)所求长度就是类中成员变量的总长度 28
4:总结涉及到的知识点。
为了分析sizeof(string)的大小,涉及到一些知识点:
====》sizeof()是静态运算符,关注的是参数类型,编译时决定了。
====》类的长度实际是类中非静态成员变量长度,无成员变量是1,有虚函数要维持虚函数表(一个指针大小),有基类时要算基类的成员吧。
====》类模板(通用的类)和模板类(实例化的类)的概念
====》可以用using对复杂的结构重新命名。
====》string的内存分配,实际上还是类中定义一个指针,真正赋值的时候去new具体的大小。
====》typename的新用法:用在模板定义中,把相关名称识别为类型。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)