结构体

格式:

结构体关键字+类型

{};

结构体声明:

struct Student
{
    char name[10];
    int age;
    char sex[3];
    float weight;
};

第二种写法:

struct Student
{
    char name[10];
    int age;
    char sex[3];
    float weight;
}s1,s2;    //创建结构体的同时创建了两个全局变量s1,s2 

结构体的自引用:

struct Node
{
 int data;
 struct Node* next;
};

对变量的创建

typedef struct Student
{
    char name[10];
    int age;
    char sex[3];
    float weight;
}Student;

上述代码的含义是:

typedef将struct Student这个结构体重新定义为Student.

struct point
{
    int x;
    int y;
}p;//声明结构体类型的同时创建变量p
struct Node
{
    int data;
    struct point p;
    struct NOde* next;
}n={10,{4,5},NULL}//结构体嵌套初始化

结构体内存对齐

1.自身类型有一个对齐值

2.自定义类型有一个对齐值(内部成员最大的一个)

3.程序有一个指定的对齐值(#pragma pack(n) 2^x)

4.程序有一个有效对齐值(指定对齐值和将要对齐值中的较小值)

//练习
struct S1
{
 char c1;  //1  +3
 int i;    //4
 char c2;  //1  +3
};
printf("%d\n", sizeof(struct S1)); //12

c1所占空间大小为1字节,i为4,c2为1,首先将c1与i对齐,对齐值为4,所以c1要补一个3,此时c1与i一共占8个字节,再与c2对齐,8是1的整数倍,所以直接加c2的大小,总共占9个字节,不是自定义类型的对齐值(4)的整数倍,c2需要补3个,最后总共就是12

结构体传参

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 	printf("%d\n", s.num);
}
//结构体地址传参
void pirnt2(struct s* ps)
{
    printf("%d\n",ps->num);
}
int main()
{
 	print1(s);  //传结构体
	print2(&s); //传地址
	return 0;
}

上面的 print1 和 print2 函数哪个好些?

答案是print2函数,因为如果参数是结构体的话,需要开辟一个空间存储结构体的,性能上会造成浪费,因此需要传结构体地址。

位段

1.位段的成员必须是 int、unsigned int 或signed int 。

2.位段的成员名后边有一个冒号和一个数字。

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};
限制条件:

1.位段不能跨字节存储

2.位段不能跨类型存储

练习:
typedef struct Test
{
    char a : 1; //1
   	int b : 4;  //4
}Test;

上述结构体所占内存大小:8

原因:两个变量的类型不同,不能共占一个字节。char为1字节,int为4字节,char要向int对齐,总共为8字节。

联合体

联合体声明:

typedef union Test
{
    char a;
    double b;
    int c;
}Test;

联合体大小为内部最大类型的大小(该联合体大小为8)

union Un1
{
    char c[5];   //5
    int i;       //4
};

c[5]的大小为5字节,i的大小为4字节,该联合体大小至少为5字节,再者该联合体对齐值为4,所以最终联合体所占空间大小为8字节。

枚举

enum Day//星期
{
 Mon,    
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};

默认Mon从0开始, 一次递增1

枚举的优点
  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量
enum Color
{
    RED=1;
    GREEN=2;
    BLUE=4;
};
enum Color clr=GREEN//yes   只能用枚举常量给枚举变量赋值
clr=5;   //error   不能用常量给枚举变量赋值