C语言结构体介绍

本文详细介绍了C语言中结构体的声明、创建与初始化,包括结构体变量的定义、内存对齐规则、结构体传参策略以及位段的使用。重点讲解了结构体成员的访问方法和内存分配原则。
摘要由CSDN通过智能技术生成

结构体类型的声明

1.结构体概念
结构体是一些值的集合,这些值称为成员变量,成员变量可以是不同类型的。
2.结构体类型的声明
如果我们要定义一本书,我们该如何定义呢?
例如:

struct book
{
char name[20];
int page;
char auth[20];
};

struct是结构体关键字,book是结构体标签名。
需要注意的是,结构体后面的;不能少。

结构体变量的创建和初始化

1.结构体变量的创建
例如:

struct book
{
char name[20];
int page;
char auth[20];
}n;
struct book s1;
int main()
{
	struct book s2;
	return 0;
}

n,s1,s2都是结构体变量,但是前两个是全局变量,在主函数内部的是局部变量,struct book是结构体类型名。
2.结构体变量的初始化
例如:

struct book
{
char name[20];
int page;
char auth[20];
}n;
struct book s1;
int main()
{
	struct book s2 = { "zhangsna",150,"lishi" };
	return 0;
}

3。结构体的特殊声明
在声明结构的时候可以不完全的声明,省略结构体的标签名.
例如:

//匿名结构体
struct 
{
int i;
char b;
int ret;
};

在使用的时候,需要有以下几点需要注意:
(1)匿名结构体类型如果没有对类型重命名的话,基本上只能使用一次。
4.结构体的自引用
例如:

struct n
{
int i;
struct n *s1;
};

结构体成员访问操作符

结构体成员访问操作符有两个,.操作符和->,第二个适用于指针。
第一个的使用方式是:变量名.成员名
第二个的使用方式是:结构体指针.成员名
例如:

struct book
{
	char name[20];
	int page;
	char auth[20];
};
int main()
{
struct book s2 = { "zhangsna",150,"lishi" };
struct book* ptr = &s2;
printf("%s %d %s", s2.name, s2.page, s2.auth);
printf("%s %d %s", ptr->name, ptr->page, ptr->auth);
	return 0;
}

结构体内存对齐

1.结构体内存对齐的规则
(1)结构体的第一个成员要对齐到结构体起始位置偏移量为0的地址处。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的对齐数与变量大小的较小值。
(3)结构体的总大小为最大对齐数的整数倍。
(4)如果结构体中嵌套了结构体的话,嵌套的结构体成员要对齐到自己成员中最大对齐数的整数倍。结构体的整体大小就是最大对齐数(包括了嵌套的结构体成员)的整数倍的地址处。
例如:

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

在这里插入图片描述
首先,结构体的第一个成员要对齐到结构体起始位置偏移量为0的地址处。
其次,int类型的变量的对齐数是4,小于8,因此,对齐数就是4,要在偏移量为4的整数倍的地址处存放。因此放在偏移量为8的地址处。
最后,结构体的总大小是成员当中最大对齐数的整数倍,结构团体的最后一个成员的地址在偏移量为8的地址处,共9个字节,不是最大对齐数的整数倍,因此,结构体的总大小为12个字节。
2.修改默认对齐数
#pragma这个预处理命令,可以可以修改编译器默认的对齐数。
例如:

#pragma pack(1)//设置默认对齐数为1
struct s
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//恢复默认对齐数
int main()
{
	printf("%zd\n", sizeof(struct s));//结果是6
	return 0;
}

结构体传参

struct s
{
	char c1;
	int i;
	char c2;
};
void print1(struct s n)//第一种传参方式,值传递
{
	printf("%c %d %c\n", n.c1, n.i, n.c2);
}
void print2(struct s* ptr)//第二种传参方式,址传递
{
	printf("%c %d %c\n", ptr->c1, ptr->i, ptr->c2);
}
int main()
{
	struct s s = { 'w',12,'h' };
	print1(s);
	print2(&s);
	return 0;
}

采用哪一种方式更好呢?
如果采用第一种方式的话,会进入print1函数时,会再次开辟空间,浪费空间和时间。
因此,结构体传参,我们一般会选择传指针。

结构体实现位段

1.什么是位段
位段的声明和结构体是类似的,不过有一些不同。
1.位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
例如:

struct s
{
	int a : 3;
	int b : 4;
	int c : 5;
	int d : 4;
};

s就是一个位段类型。

printf("%zd\n", sizeof(struct s));//结果是多少呢?

2.位段的内存分配
(1)位段的成员可以是int,unsigned int ,char 等类型的
(2)位段的空间需要按照四个字节或一个字节来开辟。
(3)位段有许多不确定因素,位段时不跨平台的,位段的可移植性较查。
例如:

struct s
{
	char a : 3;
	char  b : 4;
	char c : 5;
	char d : 4;
};

在这里插入图片描述
a占三个比特位,b占4个比特位,还剩一个比特位,不够放c,因此,再申请一个字节的空间放c,还剩下三个比特位,不够放d,因此再申请一个字节。共三个字节。
注意:用的编译器是vs
3.位段的跨平台问题
(1)int是unsigned还是signed
(2)是从左往右放,还是从右往左放
(3)剩下的空间是继续使用还是丢弃
(4)位段的最大数目不能确定(16位机器最大是16,32位机器最大是32,写成27,在16位机器上就会出问题)
4.位段使用的注意事项
不能对位段成员使用取地址操作符
几个成员可能会使用同一个字节,就会导致一些成员没有地址,一个字节才有一个地址,如果只一个比特位的话,是没有地址的。
例如:

struct s
{
	int a : 3;
	int b : 4;
	int c : 10;
	int d : 30;
};
int main()
{
	struct s n;
	//scanf("%d", &(n.a));//err
	int ret = 0;
	scanf("%d", &ret);
	n.a = ret;
	return 0;
}
  • 39
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值