字节对齐,sizeof,strlen,_countof,offsetof,_msize
一、sizeof
sizeof()返回值最好用size_t保存,在x86和x64下,该类型有不同的表示,unsigned int
(32位),unsigned __int64
(64位)
#include <iostream>
#include <string>
#include <vector>
int main()
{
int arrx[20];
auto n = _countof(arrx); //n = 20,返回值类型为unsigned int 宏_countf定义在stdlib.h,c++包含vector头文件也可以使用
//sizeof返回值的类型是size_t,unsigned int(32位),unsigned __int64(64位)
char ch1[] = "HelloWorld";
char* ch2 = ch1; //sizeof(ch2)
int arr1[] = { 1,2,3,4,5,6,7 };
int* arr2 = arr1;
std::string str1 = "HelloC++";
std::string str2;
size_t s1 = sizeof(ch1); //11,"HelloWorld\0"因为是内存占用的空间大小,所以包含最后的字符串结束符号\0,所以是10+1=11
size_t s2 = sizeof(ch2); //和编译器平台有关,sizeof(ch2)得到的是指针的大小,x64为8,x86为4
size_t s3 = sizeof(str1); //sizeof(str)得到的是string对象的大小,是一个固定值,和str具体内容无关
size_t s4 = sizeof(str2); //64位:40,32位:28
size_t s5 = sizeof(arr1); //28 一个int占4个字节,7*4=28
size_t s6 = sizeof(arr2); //指针的大小,和编译器平台有关,32位:4,64位:8
size_t s01 = sizeof((*ch1)); //1
size_t s02 = sizeof((*ch2)); //1
size_t s03 = sizeof((*arr1)); //4
size_t s04 = sizeof((*arr2)); //4 解引用得到的都是第一个元素类型的大小 char:1,int:4
size_t s7 = str1.length();//8,字符串的长度,有几个字符就是几,不包含字符串结束符\0
size_t s8 = strlen(ch1); //10,strlen(str1)错误,strlen的参数为const char*,字符串的长度,有几个字符就是几
size_t s9 = str1.size(); //8,同str.length(),size还可用于获取容器的大小
std::vector<std::string> vec;
vec.emplace_back("abcdef");
vec.emplace_back("123");
vec.emplace_back("qwert");
size_t s10 = vec.size(); //3
size_t s11 = sizeof(vec); //和元素的个数无关,64位:32,32位:16,应该是存放了四个指针(不确定)
size_t s12 = sizeof(std::vector<bool>); //bool比其他类型大1/2,即64:48,32:24,
size_t s13 = sizeof(std::vector<int>); //16,32
size_t s14 = sizeof(std::vector<std::string>); //16,32
size_t s15 = sizeof(std::vector<char>); //16,32
return 0;
}
二、字节对齐
#include <iostream>
int main()
{
struct s1 //16
{
int i;
char c;
double d;
};
struct s2 //16
{
double d;
char c;
int i;
};
struct s3 //16
{
char c;
int i;
double d;
};
struct s4 //24
{//结构体中占用空间最大的类型为8(double),1+7 + 8 + 4 = 20 不是8的倍数,所以要填充4个字节,满足sizeof(double)=8的倍数
char c;
double d;
int i;
};
std::cout << sizeof(s1) << sizeof(s2) << sizeof(s3) << sizeof(s4) << std::endl;
#pragma pack(push) //保存对齐状态 push和pop是一对,不用push和pop包住,pack(n)将会对后面的所有sizeof生效
#pragma pack(4) //设定为4字节对齐
struct t1
{
char m1;
double m4;
int m3;
};
std::cout << sizeof(t1) << std::endl; //16
#pragma pack(pop)//恢复对齐状态
struct t2
{
char m1;
double m4;
int m3;
};
std::cout << sizeof(t2) << std::endl; //24
return 0;
}
三、字符串长度
cstring头文件(C风格字符串)
strlen(str)计算字符串长度(有几个字符就返回几,不包含末尾的'\0')
string头文件(std::string)
str.size()返回字符串的长度(包括结尾的空字符,即空格也算,string类型的字符串结尾没有'\0')
CString头文件(MFC)
str.GetLength()返回的是字符个数(不包括结尾的空字符)
四、sizeof(常见类型)
1.sizeof(std::string)
名称 | X86(字节数) | X64(字节数) |
---|---|---|
Allocator | 4 | 8 |
原始字符串Data位置 | 15 + 1,最多包含15个字符加一个结束符'\0' | 15 + 1,最多包含15个字符加一个结束符'\0' |
字符长度Size | 4 | 8 |
当前容量Capacity | 4 | 8 |
总计 | 28 | 40 |
以下我们只讨论 32位程序
- 对于长度小于等于15个字符的字符串:
数据会保存到Data的总计16个字节中,如果string 是临时变量,整个字符串的数据位于栈上
- 对于数据大于15个字符的字符串:
会在堆上分配一块额外的数据区域,并将所有数据填充到堆中,然后将堆指针赋值到 Data 的第一个指针位置 (Data前4个字节)
2.sizeof(容器)
容器 | x64 | x86 |
---|---|---|
deque | 40 | 20 |
vector | 32 | 16 |
3.sizeof(class)
class X{};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y,public Z {};
sizeof(X) //1
sizeof(Y) //4
sizeof(Z) //4
sizeof(A) //8
X为1是因为编译器的处理,在其中插入了1个char,为了让其对象能在内存中有自己独立的地址Y,Z是因为虚基类表的指针A 中含有Y和Z所以是8
五、一些用来获取字节数或元素个数的方法
_countof
windows宏,用来计算一个静态分配的数组中的元素的个数,返回值的类型为 unsigned int ,必须是静态分配的数组,不能是指针
char s1[] = "12345";
int s2[] = { 1,2,3,4,5 };
int* s3 = new int[10]();
auto ret1 = _countof(s1); //6,包含'\0'
auto ret2 = _countof(s2); //5
//auto ret3 = _countof(s3); //error
offsetof
offsetof 会生成一个类型为size_t的整型常量,它是一个结构成员相对于结构开头的字节偏移量。成员是由 member-designator 给定的,结构的名称是在type 中给定的。
_msize
获取指针指向内容的实际大小(不是指针的大小),只能获取到new或malloc出来的指针指向内容的大小。
#include <iostream>
void fun(int* p)
{
std::cout << sizeof(p) << '\n'; //8 (x64)
std::cout << _msize(p) << '\n'; //40 = 4*10
}
int main()
{
int* p = new int[10]();
int p1[] = { 1,2,3 };
fun(p);
//fun(p1); //不是new或malloc出来的指针,程序会崩溃
return 0;
}