乐趣区

关于c:自定义类型结构体等

构造体

构造体定义方法

定义方法一:

struct MyStruct   // 定义构造体,定义的构造体在内存中是不占空间的
{
    int a;
    char b;
    short c;
};               // 没有创建对象

int main(void)
{
    // 应用构造体, 创立一个构造体对象,创建对象时才会占用内存空间
    struct MyStruct a = {10,'b',4};  // 能够在创立时就初始化,也能够在之后赋值
    struct MyStruct* sp = &a;  // 构造体指针
    a.a = 10;
    (*sp).b = 'b';
    sp->c = 4;
}

定义方法二:

struct MyStruct
{
    int a;
    char b;
    short c;
}s1,s2,s3; 
//s1,s2,s3 是创立的构造体对象,在定义构造体的同时创建对象
//s1,s2,s3 是全局变量      

int main(void)
{s1.a = 10;  // 这里能够间接应用构造体对象}

定义方法三:

typedef struct MyStruct   // 相当于对这个构造体重命名为 Stu
{
    int a;
    char b;
    short c;
}Stu;   

int main(void)
{Stu s1;   // 应用重命名,进行创建对象。}

构造体作为参数时

  • 函数传参时,参数是须要压栈的
  • 如果传递一个构造体对象时,间接传递构造体实参,在函数那边会压栈一个同样大小的内存空间(创立构造体形参),会导致系统开销增大,从而导致性能升高。
  • 所以传递构造体参数时,尽量传递构造体的指针(地址)

匿名构造体类型

  • 留神:如果两个匿名构造体的成员一样,然而在编译器看来也是不同的构造体类型。
struct     // 没有申明构造体的名称
{
    int a;
    char b;
    short c;    
}x;        // 定义了一个构造体的对象
// 这种申明构造体办法,只能在申明时定义构造体对象,之后不能在定义对象(没有构造体名称)struct     
{
    int a;
    char b;
    short c;    
}p;           // p 和 x 是不同的构造体类型。

构造体的自援用

struct Node
{
    int date;
    struct Node* next;   // 用指针指向下一个同类构造体的地址。//struct Node n;     // 在构造体中创立本人类型的对象
    // 这种用法是谬误的,会呈现套娃的状况,内存会无限大,零碎报错
}

构造体内存对齐

  • 先看示例:
struct X  
{
    int a;
    char b;
    short c;    
}; 
struct P   
{
    char a;
    int b;char c
    short d;  
    struct X st;
}; 
struct P a;
printf("%d\n",sizeof(p));    // 输入后果为  20

构造体对齐规定

  • 第一个成员,寄存在构造体变量偏移量为 0 的地址处(即构造体变量的首地址处)
  • 其余成员要对齐到某个数(对齐数)的整数倍的地址处。

    • 对齐数 = 编译器默认的对齐数 和 该成员大小 的较小值
    • VS 中默认对齐数为 8。
    • 每个成员都有一个对齐数(可能会不雷同)
  • 构造体的总大小为最大对齐数的整数倍。
  • 如果有嵌套构造体,嵌套构造体对齐到字节的最大对齐数的整数倍处,构造体的整体大小就是最大对齐数(包含嵌套构造体成员的对齐数)的整数倍。

为什么存在内存对齐

  • 移植起因:不是所有硬件平台都能任意地址拜访,某些硬件平台只能在某些地址处取某些特定类型的数据。
  • 性能起因:数据结构(尤其是栈)应该尽可能的在边界上对齐。为了拜访未对齐的内存,处理器须要作两次内存拜访,而对齐内存的拜访只须要拜访一次。
  • 总的来说就是:构造体的内存对齐是拿空间来换取工夫。
  • 所以在设计构造体时,尽量将空间小的成员放在一起,节俭空间。

批改默认对齐数 #pragma pack

  • 将构造体放在 #pragma pack 之间,该构造体的默认对齐数就变为 4。
#pragma pack(4)
struct X  
{
    int a;
    char b;
    short c;    
}; 
#pragma pack()

宏 offsetof() 求偏移量

offsetof(type, member-designator);
int i = offsetof(struct X,b);   // 返回值为 b 的偏移量 
  • 函数作用:求构造体成员相当于首地址的偏移量
  • type:构造体类型
  • member-designator:构造体成员
  • 返回值为 size_t,示意构造体成员的偏移量。

构造体与位段

  • 位段的定义方法和构造体相似。
  • 位段成员的前面有一个冒号和数字。(数字是这个变量的大小 bit 位)(不是字节哦)
  • 位段成员只能是 unsigned int 和 signed int 或者 char 类型。
  • 位段不存在内存对齐(节俭空间)
  • 位段是不能跨平台的,所以可移植程序中不能应用位段。
// 定义方法
struct MyStruct
{
    int _a : 2;            //_a 这个变量只占 2 个 bit 位
    int _b : 5;            //_b 这个变量只占 5 个 bit 位
    unsigned int _c : 10;  //_c 这个变量只占 10 个 bit 位
    unsigned int _d : 30;
};
// 总共 37 个 bit 位,占两个 int 型 8 字节 

枚举

  • 把可能的值一一列举进去。
// 枚举的定义方法
enum Day    // 一周
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sum
};
  • 如果定义枚举时,如果没有初始化,则默认从 0 开始顺次增大 1。
  • 枚举外面的都是常量,都是枚举类型,只能在定义时初始化,之后无奈赋值。

联合体(共用体)

  • 共用体中的成员共用同一个内存空间(即所有成员的地址都一样)
  • 留神:共用体是成员的地址雷同,并且那个空间存的数据也雷同
  • 然而每个成员的值是不同的(因为变量类型不同,解析办法不同)
union Un   // 定义共用体
{
    char c;
    int i;
    short j;
};
union Un u;
u.i = 123456;
printf("%d\n", u.c);   //64
printf("%d\n", u.i);  //123456
printf("%d\n", u.j);  //-7616
  • 留神:共用体定义时不能初始化,只能在创立共用体对象后能力赋值。

联合体的对齐

  • 共用体的总大小为最大对齐数的整数倍。
  • 共用体的总大小至多是成员变量中空间最大的那个。
union Un   // 该共用体大小为 4(int 的对齐数为 4){
    char c;
    int i;
    short j;
};

union Un   // 大小位 16,int 为 4 字节,arr 占 14 字节,所以总大小为 16 字节
{short arr[7];
    int i;
};
退出移动版