自定义类型:结构体,声明,变量初始化,结构体内存对齐。

简介: ✅<1>主页:C语言的前男友📃<2>知识讲解:结构体,声明,变量初始化,结构体内存对齐🔥<3>创作者:C语言的前男友☂️<4>开发环境:Visual Studio 2022💬<5>前言:关于结构体我们已经不陌生了,今天我们就来系统的聊一聊结构体的声明,初始化,结构体占的内存大小。

一.结构体

//结构体声明
struct Stu
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
};


67ead548cd7b46cc8ea1ad6703af9199.png


二.特殊的结构体声明

 在声明结构的时候,可以不完全的声明——匿名结构体类型。

//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}a[20], *p;

上面的两个结构在声明的时候省略掉了结构体标签。

那么问题来了?在上面代码的基础上,下面的代码合法吗?

p = &x;

注意:编译器会把上面的两个声明当成完全不同的两个类型。所以是非法的。


三.结构体自引用

 在结构中包含一个类型为该结构本身的成员是否可以呢?

struct Node
{
    int data;
    struct Node next;
};
可行否?如果可以,那sizeof(struct Node)是多少?



这里显然是不行的。但是是不是就意味着就不行了呢?还是有办法实现自引用的。

正确的结构体自引用方式
struct Node
{
    int data;
    struct Node* next;
};


四.注意:

如果加上 typedef 是不是就可以这样实现结构体自引用呢? 
typedef struct
{
  int data;
  Node* next;
}Node;
这样写代码是不行的
解决方案:
typedef struct Node
{
  int data;
  struct Node* next;
}Node;


五.结构体的定义和初始化

struct Point
{
    int x;
    int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu     //类型声明
{
    char name[15];//名字
    int age;    //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
    int data;
    struct Point p;
    struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
struct Stu
{
  char name[20];
  int age;
  char ID[20];
};
int main()
{
  按照结构体顺序初始化变量
  struct Stu stu2 = { "ikun",20,"JNTM123" };
  自定义顺序初始化变量
  struct Stu stu1 = { .ID = "CZU123",.age = 19,.name = "张三" };
  return 0;
}


六.结构体内存对齐

我们已经掌握了结构体的基本使用了,那么一个结构体到底时多大呢?到底怎么计算呢?

struct S1 
{
  char c1;
  int i;
  char c2;
};
struct S2
{
  char c1;
  char c2;
  int i;
};
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
  return 0;
}

大家可以猜一下结果


22225b594d30476c918b03631b4ace70.png


结果是不是出乎大家的意料了,虽然结构的成员都是一样的,但是结构的大小却不一样。


到底是为什么造成这样的结果呢?其实结构体的大小可不是简单的结构体成员大小加在一起的。


而是有一种内存对齐的规则。


结构体的对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整

体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。


下面我们就对着这个规则来做几个例子:

(1)例一:

struct S1
{
  char c1;
  int a;
  char c2;
};
求结构体s1的大小



运行效果:



(2)例二:

1.题目:

struct S2
{
    char c1;
    char c2;
    int i;
};
求结构体 S2 的大小


2.图解:


3.运行效果:



(3)例三:

1.题目:

struct S1
{
  char c1;
  int a;
  char c2;
};
struct S2
{
    char c1;
    struct S1 S;
    char c2;
    int i;
};
//求结构体S2的大小

2.图解:



运行结果:



七、为什么存在内存对齐

(1) 平台原因(移植原因):


不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特

定类型的数据,否则抛出硬件异常。


(2) 性能原因:


数据结构(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访

问。


总体来说:


结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

让占用空间小的成员尽量集中在一起。

例如:

struct S1
{
    char c1;
    int i;
    char c2;
};
struct S2
{
    char c1;
    char c2;
    int i;
};

s2就比s1更节省空间。


四.修改默认对齐数

#pragma pack();

例一:

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
  char c1;
  int i;
  char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
  char c1;
  int i;
  char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
  //输出的结果是什么?
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
    return 0;
}

运行结果:



例二:

#include <stdio.h>
#pragma pack(1)//设置默认对齐数为8
struct S1
{
  char c1;
  int i;
  char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
  char c1;
  int i;
  char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
  //输出的结果是什么?
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
    return 0;
}


运行结果:



最后:

不操千曲而后晓声,观千剑而后识器。


2.jpg

 

相关文章
|
存储 网络协议 编译器
自定义类型结构体(下)
自定义类型结构体(下)
60 0
|
编译器 Linux C++
自定义类型结构体(中)
自定义类型结构体(中)
41 0
|
C语言 C++
自定义类型结构体(上)
自定义类型结构体(上)
64 0
|
6月前
|
编译器 C语言
自定义类型:结构体(1)
自定义类型:结构体(1)
42 3
|
6月前
|
编译器 Linux C语言
自定义类型:结构体(2)
自定义类型:结构体(2)
37 1
|
6月前
|
C++
结构体变量与结构体变量指针作为函数参数
结构体变量与结构体变量指针作为函数参数
33 0
|
6月前
|
C语言
用结构体变量和结构体变量的指针做函数参数
用结构体变量和结构体变量的指针做函数参数
74 3
|
存储 编译器
自定义类型——结构体(二)
自定义类型——结构体
|
6月前
|
存储 编译器 Linux
结构体,自定义类型
结构体,自定义类型
55 0
|
11月前
|
网络协议 编译器 C语言
自定义类型:结构体
自定义类型:结构体
87 0