C/C++内存对齐之struct, union与class

382 阅读5分钟

strlen与sizeof

strlen() 函数计算的是字符串的实际长度,遇到第一个'\0'结束。如果你只定义没有给它赋初值,这个结果是不定的,它会从首地址一直找下去,直到遇到'\0'停止。而sizeof返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个操作符,strlen()是函数。

    cout << strlen("\a\t\n") << endl << strlen("das\0sadad") << endl;
    cout << sizeof("\a\t\n") << endl << sizeof("das\0sadad") << endl;
    const char* str1 = "123";
    const char str2[100] = "123456";
    char str3[] = "12345";
    printf("strlen(str1)=%d, sizeof(str1)=%d\n", strlen(str1), sizeof(str1));
    printf("strlen(str2)=%d, sizeof(str2)=%d\n", strlen(str2), sizeof(str2));
    printf("strlen(str3)=%d, sizeof(str3)=%d\n", strlen(str3), sizeof(str3));
    
    string s = "er3\\32424\wer\a\tsd\nerew\rer\0fsdfsd\123sfe";
    cout << s << endl;

输出:

3
3
4
10
strlen(str1)=3, sizeof(str1)=8
strlen(str2)=6, sizeof(str2)=100
strlen(str3)=5, sizeof(str3)=6
er3\32424wer    sd
erew

sizeof与struct

#include <iostream>
#include "stdio.h"
#include <stdlib.h>
using namespace std;

/*
内存对齐的主要作用是:
1、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;
    某些硬件平台只能在某些地址处取某些特定类型的数据,
    否则抛出硬件异常。
2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
    CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,
    因此CPU在读取内存时是一块一块进行读取的。
    块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。
*/

/*
结构体成员按照定义时的顺序依次存储在连续的内存空间,
但是结构体的大小并不是简单的把所有成员大小相加,而是遵循一定的规则,
需要考虑到系统在存储结构体变量时的地址对齐问题。
*/

struct stru_empty
{

};
struct stru1
{
    int a; //start address is 0
    char b; //start address is 4
    int c; //start address is 8
};
struct stru2
{
    int i; //start address is 0
    short m; //start address is 4
};
struct stru3
{
    char i; //start address is 0
    int m; //start address is 4
    char n; //start address is 8
};
// 结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。
struct stru4
{
    char i; //start address is 0
    char n; //start address is 1
    int m; //start address is 4
};

struct stru5
{
    short i;
    struct
    {
        char c;
        int j;
    } ss;
    int k;
};

/*
四、对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:
       (1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
       (2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。
*/
struct stru6
{
    char i;
    struct
    {
        char c;
        int j;
    } tt;
    char a;
    char b;
    char d;
    char e;
    int f;
};
struct stru7
{
    char i;
    struct
    {
        char c;
        //int j;
    } tt;
    char a;
    char b;
    char d;
    char e;
    int f;
};
// 五:另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开
struct Array
{
    float f;
    char p;
    int arr[3];
};

int main()
{
    struct stru6 st6;
    struct stru7 st7;
    struct Array ar;
    printf("sizof(char)=%d \n", sizeof(char));
    printf("sizof(int)=%d \n", sizeof(int));
    printf("sizof(short int)=%d \n", sizeof(short int));
    printf("sizof(long int)=%d \n", sizeof(long int));
    printf("sizof(long)=%d \n", sizeof(long));
    printf("sizof(float)=%d \n\n", sizeof(float));

    printf("sizof(stru_empty)=%d \n", sizeof(stru_empty));
    printf("sizof(stru1)=%d \n\n", sizeof(stru1));
    printf("sizof(stru2)=%d \n\n", sizeof(stru2));
    printf("sizof(stru3)=%d \n\n", sizeof(stru3));
    printf("sizof(stru4)=%d \n\n", sizeof(stru4));
    printf("sizof(stru5)=%d \n\n", sizeof(stru5));
    printf("sizof(stru6)=%d \n", sizeof(stru6));
    printf("sizof(stru6.tt)=%d \n", sizeof(st6.tt));
    printf("the address of stru6.i=%d \n", &st6.i);
    printf("the address of stru6.a=%d \n\n", &st6.a);

    printf("sizof(stru7)=%d \n", sizeof(stru7));
    printf("sizof(stru7.tt)=%d \n", sizeof(st7.tt));
    printf("the address of stru7.i=%d \n", &st7.i);
    printf("the address of stru7.a=%d \n\n", &st7.a);

    printf("sizof(ar)=%d \n", sizeof(ar));
    printf("sizof(ar.f)=%d \n", sizeof(ar.f));
    printf("sizof(ar.arr)=%d \n", sizeof(ar.arr));
    return 0;
}

输出:

sizof(char)=1
sizof(int)=4
sizof(short int)=2
sizof(long int)=4
sizof(long)=4
sizof(float)=4

sizof(stru_empty)=1
sizof(stru1)=12

sizof(stru2)=8

sizof(stru3)=12

sizof(stru4)=8

sizof(stru5)=16

sizof(stru6)=20
sizof(stru6.tt)=8
the address of stru6.i=768603928
the address of stru6.a=768603940

sizof(stru7)=12
sizof(stru7.tt)=1
the address of stru7.i=768603976
the address of stru7.a=768603978

sizof(ar)=20
sizof(ar.f)=4
sizof(ar.arr)=12

sizeof与union

sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。

sizeof与class

简单例子

class Data
{
    char c;
    int a;
};
// cout << sizeof(Data) << endl; // 输出8
class Data
{
    char c;
    double a;
};
// cout << sizeof(Data) << endl; // 输出16

注意声明顺序

class Data
{
    char c;
    int a;
    char d;
};
// cout << sizeof(Data) << endl; // 输出12
class Data
{
    char c;
    char d;
    int a;
};
// cout << sizeof(Data) << endl; // 输出8

嵌套型

class BigData
{
public:
    char array[33];
};

class Data
{
public:
    BigData bd;
    int integer;
    double d;
};



int main()
{
    cout << sizeof(BigData) << "   " << sizeof(Data) << endl;
    Data DD;
    cout << &DD.bd << endl << &DD.integer << endl << &DD.d << endl;
}

/*
输出
33   48
000000A79F6FFC08  // offset=0
000000A79F6FFC2C  // offset=0x2C-0x08=2*16+12-8=36
000000A79F6FFC30  // offset=0x30-0x08=40, 0x30-0x2C=4, sizeof(double)=8
*/
class BigData
{
public:
    char array[33];
};

class Data
{
public:
    BigData bd;
    double d;
};



int main()
{
    cout << sizeof(BigData) << "   " << sizeof(Data) << endl;
    Data DD;
    cout << &DD.bd << endl << &DD.d << endl;
}

/*
输出
33   48
0000004420F8FC78 // offset=0
0000004420F8FCA0  // offset=0xA0-0x78=40
*/

sizeof计算栈空间

参考C++ sizeof计算class大小 | C++: class sizeof

1 类与结构一样,都有字节对齐的问题
2 类中普通的函数不占用类的大小
3 类中虚函数占用一个地址位宽(4或8字节),且不论有多少个虚函数一共只占这么多
4 类中常量不占用类大小: const static / enum{aa=2,bb=4};
5 类中static修饰的变量不占用类大小,因为修饰后存储在静态区域
6 子类的大小等于子类新增的加上父类的大小

1 sizeof是用来计算栈大小,不涉及全局区,故类的静态成员大小sizeof不涉及。
2 本题中的虚函数属于同一个类,故只需要一个指针指向虚函数表,所以在64位系统中占用8个字节。就算本题有100个虚函数,那么也只占用8个字节。
综上,占用栈空间的成员有:a, p, func1, func2,由于64位对其,故总空间为8+8+8=24字节。

sizeof(类)计算的是类中存在栈中的变量的大小,而类中的b*c都是static静态变量,存在全局区中,因此不在计算范围之内,于是只剩下char a,void *p和两个virtual虚函数,a是char类型,占用一个字节,p是指针,在64位系统的指针占用8个字节,而两个虚函数只需要一个虚函数表指针,也是八个字节,加上类中的对齐方式(char a对齐时后面补上7个字节),故答案为24.