在C语言中,或者说在我们平时使用的基本类型中,数组的长度都可以用 sizeof(arr)/sizeof(arr[0])
来求得。因为sizeof运算符可以求出每个对象所占内存的字节数,并且在这些基本类型组成的数组中,每个元素所占内存空间都是相同的,因此我们可以使用 “数量 = 总价 / 单价” 这种方式来计算。
那么在C++中我们可不可以这样做呢?
请看用例:
可以看到,使用这种方式我们成功求出了strArray中元素的个数。
而我们都知道的string字符串数组中每个string都是可变长度的,按理说使用 string 创建的数组每个元素所占空间大小都是不同的,那为什么仍然可以采用这种方式求成员个数呢?
string类型是个模板类( using string = basic_string
) ,其中最后一个模板参数指定类申请内存的方式。
一个简答的示例:最初str指向"aa…",数据部分地址在 str.data() 中,str对象地址为str本身。
重新填充数据后,原空间不足以容纳200个’z’将重新申请空间。
在输出的结果中,可以发现。str对象的地址没有改变,只有数据部分地址改变了。
因此可以说string对象存储的内容大小与其所占的空间(栈空间)是没有关系的,因为数据部分存储在堆区中(空间适配器决定),换句话说在一个string的数组中每个string字符串所占的空间相同。
参考如下示例:
string s1[] = { "1" };
string s2[] = { "1","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" };
string s3[] = { "1","2","3" };
cout << sizeof(s1) << " " << sizeof(s1[0]) << endl;
cout << sizeof(s2) << " " << sizeof(s2[0]) << " " << sizeof(s2[1]) << endl;
cout << sizeof(s3) << " " << sizeof(s3[0]) << " " << sizeof(s3[1]) << " " << sizeof(s3[2]) << endl;
// 输出
// 28 28
// 56 28 28
// 84 28 28 28
我们可以看到,s1所占总空间为 28 字节,而它只有一个成员 “1”,所占空间即为整个数组的空间。
s2所占空间为 56 字节,而它有两个元素,每个元素都占 28 字节,整个数组所占空间为 28 * 2 。
s3所占空间为 84 字节,而它有三个元素,每个元素都占 28 字节,整个数组所占空间为 28 * 3 。
如此以来,我们便可以使用 sizeof(s) / sizeof(s[0])
来计算string数组的长度了。
string s1[] = { "1" };
string s2[] = { "1","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" };
string s3[] = { "1","2","3" };
cout << "s1 size = " << sizeof(s1) / sizeof(s1[0]) << endl;
cout << "s2 size = " << sizeof(s2) / sizeof(s2[0]) << endl;
cout << "s3 size = " << sizeof(s3) / sizeof(s3[0]) << endl;
C/C++中定义了宏函数 _countof 用于求数组元素个数。
// stdlib.h
#ifndef _countof
#define _countof __crt_countof
#endif
// vcruntime.h
#ifdef __cplusplus
extern "C++"
{
template <typename _CountofType, size_t _SizeOfArray>
char (*__countof_helper(_UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
#define __crt_countof(_Array) (sizeof(*__countof_helper(_Array)) + 0)
}
#else
#define __crt_countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#endif
其中在C语言中,使用的是 __crt_countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
与我们之前讨论的方法相同。
而在C++中的代码利用了C++模板的特性。
template
我们可以类比这个宏函数的定义,写一个模板函数:
template <typename Type, size_t size>
void func(Type (&var)[size]) // 引用传参:type被解析成了元素类型,如int,而size被解析成了元素个数。这就像是使用typeid查看数组一样,
{
cout << size << endl;
}
// 按照模板可以解析参数类型的原理,我们可以写出一个得到数组元素个数的函数。
template <typename Type, size_t size>
inline size_t getLength(const Type (&var)[size])
{
return size;
}
string对象自身提供了一种 s.size()
的方法(等价于length方法),用于获取字符串的长度。可以通过s[0].size()的方式获得其数组中s[0]字符串的长度。
string s[] = { "hello","world!" };
cout << s->size() << endl; // 等价于s[0].size()
cout << s[0].size() << endl; // 5
cout << s[1].size() << endl; // 6
其次,如果我们想要遍历 string [] 形式数组,可以采用以下几种方式:
string strArray[] = { "hellooooooo","happy","test","xxxx","yy" };
// 法一:
int size = _countof(strArray);
for (int i = 0; i < size; ++i)
{
cout << strArray[i] << " ";
}
cout << "\n";
// 法二:
for (string& str : strArray)
{
cout << str << " ";
}
cout << "\n";
// 法三:
for (auto it = begin(strArray); it != end(strArray); ++it)
{
cout << *(it) << " ";
}
cout << "\n";
对于vector类型,这是一种数组类型,它提供了 v.size()
方法可以获取数组长度。
vector<vector<int>> v{ {1,2,3},{4,5,6,7,8},{9,10} };
cout << v.size() << endl; // 3
cout << v[1].size() << endl; // 5
vector类型是一种容器适配器,其本身就是数组类型,因此类中提供了获取元素个数的方法。
而string类型同int类型,double类型一样可以作为一种基本的类型,封装到vector数组中使用。