一.结构体
struct Test
{
int a;
int b;
int c;
};
关键字 struct ,表示其是一个结构体,后面是一个可选的标记(Test)
strcut Test Code
strcut Test 的作用类似于 int 或float的 声明。
a.在定义之后跟变量名,声明结构的过程和定义结构变量的过程可以使用合并成一步
struct Test
{
int a;
int b;
int c;
}Code;
b.不使用标记Tag, 直接写变量名
struct
{
int a;
int b;
int c;
}Code;
如果想多次使用一个结构模板,就需要使用带有标记的形式或者使用typedef
二.typedef 的作用
1.typedef的使用
typedef struct Simple
{
int a;
int b;
int c;
}simple;
可以使用 simple 代替 struct Simple
//举例 用typedef 声明 初始化
typedef struct
{
int a;
int b;
int c;
}complex;
complex test = {2,4,7};
2.结构体的初始化
struct Person
{
char *name;
int age;
double heigth;
};
(1)定义的同时初始化
struct Person p1 = {"luban7hao",3,100};
(2)先定义再逐个初始化
struct Person p2;
p2.name = "luban8hao";
p2.age = 4;
p2.heigth = 101;
(3)定义后全部初始化
struct Person p3;
p3 = (struct Person){"luban9hap",5,102};
注意:
结构体和数组在这里的区别,数组不能先定义再进行一次性初始化
结构体要明确的告诉系统{}中是一个结构体
(4)将数据赋给指定的属性
struct Person p4 = {.heigth=104, .name="luban10ha0", .age=6};
3.结构体数组
(1)声明
struct book library[MAXBKS];
(2)示例
#include <stdio.h>
#define MAXTITL 40
#define MAXAUTL 30
#define MAXBKS 100 /*最多书籍的册数*/
struct book
{
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main(int argc, char const *argv[])
{
struct book library[MAXBKS]; /*结构数组*/
int count = 0;
int index;
printf("Please enter the book title.\n");
printf("Press [enter] at the start of a line to stop.\n");
while(count < MAXBKS && gets(library[count].title) != NULL && library[count].title[0] != '\0')
{
printf("Now enter the author.\n");
gets(library[count].author);
printf("Now enter the value.\n");
scanf("%f", &library[count++].value);
while(getchar() != '\n'){
continue;
}
if (count < MAXBKS)
{
printf("Enter the next title.\n");
}
}
if (count > 0)
{
printf("Here is the list of your books: \n");
for (index = 0; index < count; index++)
{
printf("%s by %s: $%.2f\n", library[index].title, library[index].author, library[index].value);
}
}else{
printf("No books?Too bad.\n");
}
return 0;
}
4.结构体指针
(1)声明
struct guy *him; // 指向结构体的指针
(2)示例
#include <stdio.h>
#define LEN 20
struct names {
char first[LEN];
char last[LEN];
};
struct guy {
struct names handle;
char favfood[LEN];
char job[LEN];
float income;
};
int main(int argc, char const *argv[])
{
struct guy fellows[2] = {
{
{"Ewen", "Villard"},
"grilled salmon",
"personality coach",
58112.00
},
{
{"Rodney", "Swillbelly"},
"tripe",
"tabloid editor",
2344422.00
}
};
struct guy *him; // 指向结构体的指针
printf("address #1: %p #2: %p\n", &fellows[0], &fellows[1]);
him = &fellows[0];
printf("pointer #1: %p #2: %p\n", him, him + 1);
printf("him->income is $%.2f: (*him).income is $%.2f\n", him->income, (*him).income);
printf("him->favfood is %s: him->handle.last is %s\n", him->favfood, him->handle.last);
return 0;
}
5.访问结构体成员
访问成员,2种方式:
使用->,如him->favfood使用(*him).income,注意必须用圆括号,因为.运算符比*的优先级更高
三.位段
1.定义与声明:
位段的声明与结构体类似,但是其成员是一个或者多个位的字段。这些不长度的字段实际上存储在一个或者多个的整形变量中。
2.位段的使用
struct Test
{
unsigned int f1 :1;
unsigned int f2 :1;
unsigned int f3 :1;
unsigned int type :4;
unsigned int index :9;
};
我们定义了一个结构 Test
该结构定义了五个成员。
第一个成员叫做 f1, 是 unsigned int 类型的。 规定了它以 1 位存放。
成员f2 和 f3 被定义为长度只有 1 位的。定义成员 type 占有 4 位。定义成员 index 占有 9 位。
C 编译器自动地把上面的位段定义压缩在一起。位段的划分如图所示。Test总共使用了 16 位。
这种方法的好处是, 定义成 Test类型的变量的位段, 可以如引用一般的结构成员一样方便地引用。同时, 使用了更少的内存单元数。3.特殊情况
在包含位段的结构中, 也可以包括 "通常的" 数据类型。因此, 如果我们想定义一个结构, 它包含一个 int, 一个 char, 和二个 1 位的标志:
struct Test
{
int a;
int b;
unsigned f1:1;
unsigned f2:1;
};
当位段出现在结构体中,被压缩成一个字,如果某个位段无法放入一个字中,则该字的剩余部分不用,直接跳入下一个字中。
举例:利用位段和联合体 完成Float To Int 转换
//参考博客 https://blog.csdn.net/huai1693838234/article/details/44804459 用C++实现
//float型数据在计算机中以IEEE754标准存储
//32位长度,1位实数符号位(原码表示),1位指数符号位,7位指数位(此八位为移码表示)以及23位数据位(补码表示)
union Bit
{
float val;//将要强转的值
struct H
{
//由于机器是小端
//小端简单地说 就是高地址存高位 低地址存低位 结构体成员是向高地址增长的
//所以用如下方式获取
unsigned int tail:23;//获取val的最后23位 即尾码
unsigned int offset:8;//获取val的中间8位 即阶码+偏移量
unsigned int flag:1;//获取val的第一位 即符号位
}H;
};
//abs()函数:求绝对值(整数)
//exp()函数:e的次幂函数(以e为底的x次方值) 在另一种方法求float to int 会使用到,此次作业暂时不涉及。
int FloatToInt2(float val)
{
if(abs(val) < 0.000001)//如果为0值 直接返回
{
return 0;
}
int res;//保存返回值
Bit temp;
temp.val = val;
int offset = temp.H.offset - 127;//获得偏移量
res = temp.H.tail;
res |= 0x00800000;//将丢弃的1填充回来
res = res >> (23-offset);//移位操作 舍弃小数点后的数字
if(temp.H.flag == 1)//负数
{
res = -res;
}
return res;
}
int main()
{
printf("%d\n",FloatToInt2(-12.7));
getchar();
return 0;
}
使用位段时, 必须注意下列事项:
1.在某些机器上, 位段总是作为 unsigned 处理, 而不管它们是否被说明成 unsigned 的。
2.大多数C 编译器都不支持超过一个字长的位段。
3.位段不可标明维数; 即, 不能说明位段数组, 例如 flag:l[2]。
4.最后, 不可以取位段地址。原因是, 在这种情况不, 显然没有称作为 "位段指针" 类型的变量。
四.联合体
联合的声明与结构体类似,但是联合的所有成员 引用的是内存中相同的位置
不同时刻把不同的东西存储在同一个位置时,可以使用联合。
union (联合体)
{
}
sizeof(union) //最大的一个元素的大小
五.枚举类型
enum abc { a,b,c,}
//如果给中间的值 例如:b 赋值 b=4 ,则再输出的时候只能给后面的值 c+1, 则c =5,而前面的a不能-1;
//就是一个常量和限制
六.关于内存对齐的使用规则
(1)前面的地址必须是后面的地址的整数倍,不是就补齐
(2)整个struct的长度必须是最长字节的正数倍!~~
(3)如程序中有#pragma pack(n)预编译指令,则所有成员对齐以n字节为准(即偏移量是n的整数倍),
不再考虑当前类型以及最大结构体内类型
注意:关于pragma pack(n)的使用
(1)n必须是已有的大小 ,如:1,2,4,8 等
(2)如果指定的大小>默认的大小,是以默认的为准
(3)如果指定的大小<默认的大小,以指定的为准