自定义类型(九)


自定义类型(九)

自定义类型:结构体,枚举,联合

结构体

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体成员可以是标量,数组,指针,其他结构体。

结构的声明

形式1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct 结构体名
{
成员列表;
};

//例如描述一个学生
#include <stdio.h>
struct Stu
{
//定义一个结构体类型
char name[20];//姓名
short age;//年龄
char sex[5];//性别
}s1,s2,s3;//创建了三个全局的结构体变量:s1,s2,s3
int main()
{
struct Stu s;//创建局部结构体变量
return 0;
}

形式2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct 结构体名
{
成员列表;
}结构体别名;
//例如描述一个学生
#include <stdio.h>
typedef struct Stu
{
//定义一个结构体类型
char name[20];//姓名
short age;//年龄
char sex[5];//性别
}Stu;
int main()
{
Stu s1;//等同于struct Stu s1;
return 0;
}

特殊的声明(不建议用)

1
2
3
4
5
6
7
//匿名结构体类型
struct
{
char name[20];
short age;
char sex[5];
}s;//匿名结构体类型只能在这里创建变量

结构体的自引用

1
2
3
4
5
6
7
8
9
10
11
12
13
//错误实例
struct S
{
int num;
struct S next;//死递归
};

//正确实例
struct S
{
int num;
struct S* next;
};

结构体变量的定义和初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
struct A
{
int a;
char s[20];
};
struct B
{
char c[20];
struct A a1;
int d;
};
int main()
{
struct B b1={"hello",{30,"hello world"},50};//初始化
//访问结构体成员:
//结构体变量.结构体成员
printf("%s\n",b1.c);
printf("%d\n",b1.a1.a);
printf("%s\n",b1.a1.s);
printf("%d\n",b1.d);
return 0;
}
/*
hello
30
hello world
50
*/

结构体成员的访问

  1. 结构体变量.结构体成员
  2. 结构体指针->成员名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
typedef struct Stu
{
char name[20];
int age;
char sex[5];
}Stu;
void print1(Stu s)
{
printf("%s\n",s.name);
printf("%d\n",s.age);
printf("%s\n",s.sex);
}
void print2(Stu* p)
{
printf("%s\n",p->name);
printf("%d\n",p->age);
printf("%s\n",p->sex);
}
int main()
{
Stu s1={"张三",20,"男"};//初始化
print1(s1);
print2(&s1);
return 0;
}
/*打印结果
张三
20

张三
20

*/
//以上print1和print2两个函数尽量用print2
//传值时,相当于拷贝了一份结构体过去,传址时,是拷贝了一份地址,所以传址性能更高
//所以结构体传参时,尽量选择传结构体的地址

结构体内存对齐

对齐数就是编译器默认的对齐数与该成员大小的较小值。
vs默认对齐数为8。
gcc没有默认对齐数,直接将成员大小作为对齐数。
对齐规则

  • 结构体第一个成员在与结构体变量偏移量为0的地址处。
  • 其他成员要对齐到对齐数的整数倍的地址处。
  • 结构体总大小为最大对齐数的整数倍。
  • 嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的大小就是最大对齐数(含嵌套结构体的对齐数)的整数倍

例:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
struct A
{
double d;//double类型大小为8,vs默认对齐数为8,所以对齐数8
char a;//char类型大小为1,vs默认对齐数为8,所以对齐数1
int i;//int类型大小为4,vs默认对齐数为8,所以对齐数4
};
int main()
{
printf("%d\n",sizeof(struct A));//16
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
struct A
{
int n;//4/8 4
char ch;//1/8 1
int t;//4/8 4
};
struct B
{
double d;//8/8 8
char a;//1/8 1
int i;//4/8 4
struct A a1;//4/8 4
};
int main()
{
printf("%d\n",sizeof(struct B));//32
return 0;
}

:为节省空间,在创建结构体时,尽量让占用空间小的成员集中在一起。

#pragma pack()

当结构在对齐方式不合适时,可以更改默认对齐数

1
2
3
4
5
6
7
8
9
10
11
12
//#pragma pack()用来修改默认对齐数
#include <stdio.h>
struct A
{
double d;
char c;
};
int main()
{
printf("%d\n",sizeof(struct A));//16
return 0;
}

修改默认对齐数后

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#pragma pack(2)//设置默认对齐数为2,一般设的对齐数是2,4,8,16这样的数字
struct A
{
double d;
char c;
};
#pragma pack()//恢复到原来默认的对齐数
int main()
{
printf("%d\n",sizeof(struct A));//10
return 0;
}

C库宏:offsetof()

offsetof()可以给出一个结构成员相对于结构开头的字节偏移量。
头文件:stddef.h
offsetof(结构体名字, 成员名字)
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stddef.h>
struct A
{
double d;
char c;
};
int main()
{
printf("%d\n",offsetof(struct A,d));//0
printf("%d\n",offsetof(struct A,c));//8
return 0;
}

位段

位段的声明和结构体类似,但有两点不同:

  1. 位段的成员必须是int,unsigned int,signed int或者char。
  2. 位段的成员名后边有一个冒号和一个数字,数字代表多少二进制位。

内存分配

  1. 位段的成员可以是int,unsigned int,signed int或者char。
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
  3. 位段不具有跨平台性,注意可移植的程序应该避免使用位段。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
struct A
{
int a:5;
int b:10;
int c:15;
int d:20;
};
struct B
{
char a:2;
char b:4;
char c:5;
char d:7;
};
int main()
{
printf("%d\n",sizeof(struct A));//8
printf("%d\n",sizeof(struct B));//3
return 0;
}

:与结构体相比,位段可以很好的节省空间,但有跨平台的问题存在

枚举

枚举就是一一列举。

定义格式

1
2
3
4
5
6
enum 枚举名
{
枚举成员1,
枚举成员2,
……
};

第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
也可以在定义枚举类型时改变枚举元素的值。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
enum Sex
{
MALE,//0
FEMALE,//1
SECRET//2
};
enum color
{
BLUE,//0
RED=3,//3
GREEN //4
};
int main()
{
printf("%d %d %d\n",MALE,FEMALE,SECRET);//0 1 2
printf("%d %d %d\n",BLUE,RED,GREEN);//0 3 4
return 0;
}

联合(共用体)

联合是一种特殊的自定义数据类型,联合定义的变量包含一系列的成员,这些成员公用同一块空间,任何时候只能有一个成员带有值。

定义

1
2
3
4
5
union 联合名
{
成员列表;

};

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//判断大端小端
#include <stdio.h>
int check()
{
union un
{
int i;
char c;
}u1;
u1.i=1;
return u1.c;
}
int main()
{
int ret=check();
if(ret==1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

联合体的大小

  • 联合的大小至少是最大成员的大小
  • 当最大成员的大小不是最大对齐数的整数倍时,对齐到最大对齐数的整数倍

例:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
union un
{
int a;//大小为4,对齐数为:4/8 4
char s[7];//数组的对齐数看数组元素
//大小为7,对齐数为:1/8 1
};
int main()
{
printf("%d\n",sizeof(union un));//8
return 0;
}

Author: ljs
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source ljs !
评论
  TOC