c语言结构体

1. 结构体的声明

1.1 结构体的基础知识

结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量。

1.2 结构的声明

struct tag
{
	member - list;
}variable-list;

例:描述一个人的信息:名字+电话+性别+身高

//声明的结构体类型struct peo
struct peo
{
	char name[20];//名字
	char tela[12];//电话
	char sex[5];//性别
	int high;//学号
}p1, p2;//分号不能丢

其中p1和p2是使用struct peo结构类型创建的两个变量,可以也就是p1和p2分别可以放一个人的信息。 但是因为p1和p2放在大括号的外面,所以p1和p2是两个全局的结构体变量。要想使其成为局部变量,我们可以使用下面这种方法创建结构体变量。

struct peo
{
	char name[20];//名字
	char tela[12];//电话
	char sex[5];//性别
	int high;//学号
};//分号不能丢​
int main()
{
	struct peo p1 = { 0 };//结构体变量的创建
	return 0;
}

1.3 结构体成员的类型

结构的成员可以是标量、数组、指针,甚至是其他的结构体。

struct peo
{
	char name[20];//数组

};

struct st
{
	struct peo p;//其他结构体
	int num;//标量
	float* f = num;//指针
};

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

根据上面那个例题,我们可以如下初始化:

#define<stdio.h>
struct peo
{
	char name[20];//名字
	char tela[12];//电话
	char sex[5];//性别
	int high;//学号
};//分号不能丢​
int main()
{
	struct peo p1 = { "暴龙战士","88888888888","男",250};//结构体变量的创建

	return 0;
}

结构体成员包含结构体的结构体变量初始化:

我们需要再用一个{ }将结构体中包含的结构体初始化。

操作如下: 

#define<stdio.h>
struct peo
{
	char name[20];//名字
	char tela[12];//年龄
	char sex[5];//性别
	int high;//学号
};//分号不能丢​
struct st
{
	struct peo p;
	int num;
};
int main()
{
	struct st s = { {"暴龙战士","88888888888","男",250},66 };

	return 0;
}

初始化后我们可以按照以下的方法将其打印出来:

#define<stdio.h>
struct peo
{
	char name[20];//名字
	char tela[12];//年龄
	char sex[5];//性别
	int high;//学号
};//分号不能丢​
struct st
{
	struct peo p;
	int num;
};
int main()
{
	struct peo p1 = { "暴龙战士","88888888888","男",250};//结构体变量的创建
	struct st s = { {"暴龙战士","88888888888","男",250},66 };
	printf("%s %s %s %d \n",p1.name,p1.tela,p1.sex,p1.high );
	printf("%s %s %s %d %d\n", s.p.name, s.p.tela, s.p.sex, s.p.high, s.num);
	return 0;
}

也就是:结构体变量+.+成员变量。 

 

2.结构体成员的访问

如果是结构体指针访问指向对象的成员:就用  结构体指针 -> 成员变量  的形式打印

如果是结构体变量访问成员:就用  结构体变量 . 成员变量  的形式打印

具体操作如下:

#define<stdio.h>
struct peo
{
	char name[20];//名字
	char tela[12];//年龄
	char sex[5];//性别
	int high;//学号
};//分号不能丢​
//结构体指针访问指向对象的成员
void print1(struct peo* sp)
{
	printf("%s %s %s %d \n", sp->name, sp->tela, sp->sex, sp->high);
    //结构体指针 -> 成员变量
}
//结构体变量访问成员
void print2(struct peo p)
{
	printf("%s %s %s %d \n", p.name, p.tela, p.sex, p.high);
    //结构体变量 . 成员变量
}
int main()
{
	struct peo p1 = { "暴龙战士","88888888888","男",250};//结构体变量的创建
	
	print1(&p1);
	print2(p1);
	return 0;
}

3.结构体传参

我们知道形参是实参的临时拷贝,如果使用结构体变量访问成员时形参就会将实参拷贝一份,这样就需要多开辟一块空间,就会结构体过大,参数压栈的系统开销比较大,从而导致性能的下降。

而使用结构体指针访问指向变量的成员时(如上述代码),我们只是将&p1(4个或8个字节)的地址传给sp而已。

结论:结构体传参的时候,要传结构体的地址。

4.结构体数组  

使用结构体变量建立数据类型后,我们就可以利用这个数据类型创建数组,就比如创建整形数组就可以写为 int arr[ ],那么创建一个自己建立的数据类型的数组就可以写为:

声明的结构体类型名 + 数组名。

例如我们声明一个结构体类型struct my后,就可以在主函数写一个struct my类型的数组。如下:

struct my
{
int a;
char b;
int arr[5];
};
int main()
{
struct my brr[5];
}

 5. 结构体内存对齐

5.1 对齐规则

⾸先得掌握结构体的对⻬规则:

1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

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

对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

- VS 中默认的值为 8

- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

我们可以尝试判断一下,如下结构体的大小。

struct S
{
	char a;
	int b;
	char c;
};
int main()
{
	printf("%zd ", sizeof(struct S));
	return 0;
}

5.2 为什么存在内存对齐 

  1. 平台兼容性:不同的硬件平台对于内存的访问有不同的要求。某些硬件平台只能在某些特定的地址上访问特定类型的数据。如果数据没有对齐到适当的地址,可能会导致硬件异常或性能下降。
  2. 性能优化:内存对齐可以提高数据访问的速度。如果数据对齐到CPU的字边界,那么CPU可以一次性读取或写入数据,而不需要进行多次内存操作,从而提高了处理速度。
  3. 编译器默认行为:编译器在设计时会考虑到上述因素,因此会默认进行内存对齐。编译器会根据特定的规则(如结构体成员的类型和顺序)来决定如何对齐结构体中的数据。
  4. 减少内存碎片:适当的内存对齐可以减少内存碎片,提高内存的使用效率。
  5. 跨平台开发:在进行跨平台开发时,内存对齐可以确保代码在不同的系统和硬件平台上都能正确运行,避免了因平台差异导致的错误。
  6. 遵守ABI约定:应用程序二进制接口(Application Binary Interface, ABI)通常包含对内存对齐的要求,以确保不同编译单元之间能够正确地交互。
  7. 避免未定义行为:未对齐的数据访问在C语言标准中是未定义行为,可能导致程序崩溃或其他不可预见的结果。

5.3 修改默认对齐数

 #pragma 这个预处理指令,可以改变编译器的默认对⻬数。

#pragma pack(1)//设置默认对⻬数为1

     例题1:

(1)定义一个结构体变量Birthday用来存放一个人的出生日期(年、月、日)。

(2)定义一个结构体变量Student 用来存放学生个人信息,包括姓名(10个字符),学号(长整型)、出生日期、身高、体重)

(3)定义一个结构体数组MyClass用来存放5个学生的信息

编写程序实现要求从键盘输入5个同学的信息,然后输出5个同学的信息。

#define<stdio.h>
struct Birthday
{
	int year;
	int	month;
	int day;
};
struct Student
{
	char name[10];//名字
	long int num;//学号
	struct Birthday p;//出生日期
	int hight;//身高
	int weight;//体重
};

int main()
{
	struct Student MyClass[5];
	for (int i = 0; i < 5; i++)
	{
		printf("输入第%d个学生的信息:\n ", i + 1);
		printf("名字:");
		scanf("%s", &MyClass[i].name);
		printf("学号:");
		scanf("%ld", &MyClass[i].num);
		printf("生日日期(年 月 日):");
		scanf("%d %d %d", &MyClass[i].p.year, &MyClass[i].p.month, &MyClass[i].p.day);
		printf("身高:");
		scanf("%d", &MyClass[i].hight);
		printf("体重:");
		scanf("%d", &MyClass[i].weight);
	}
	printf("\n");
	for (int j = 0; j < 5; j++)
	{
		printf("输出第%d个学生的信息:\n", j + 1);
		printf("名字:");
		printf("%s\n", MyClass[j].name);
		printf("学号:");
		printf("%ld\n", MyClass[j].num);
		printf("生日日期(年 月 日):");
		printf("%d %d %d\n", MyClass[j].p.year, MyClass[j].p.month, MyClass[j].p.day);
		printf("身高:");
		printf("%d\n", MyClass[j].hight);
		printf("体重:");
		printf("%d\n", MyClass[j].weight);
	}
	return 0;
}

例题2:

 有5个学生,每个学生的数据包括学号、姓名、3门课的成绩。从键盘输入5个学生数据,要求输出3门课程总平均成绩,以及最高分的学生的数据(包括学号、姓名、3门课程的成绩、平均分数)。 要求用一个input函数输入5个学生数据,用一个 average 函数求总平均分,用 max 函数找出最高分学生数据。总平均分和最高分的学生的数据都在主函数中输出。

#define<stdio.h>
#define n 5
struct stu
{
	long int num;//学号
	char name[10];//名字
	int a;//科目一
	int b;//科目二
	int c;//科目三
};
void input(struct stu arr[n])
{
	for (int i = 0; i < n; i++)
	{
		printf("输入第%d个学生的数据:\n", i + 1);
		printf("输入学号:");
		scanf("%ld", &arr[i].num);
		printf("输入名字:");
		scanf("%s", &arr[i].name);
		printf("输入科目一:");
		scanf("%d", &arr[i].a);
		printf("输入科目二:");
		scanf("%d", &arr[i].b);
		printf("输入科目三:");
		scanf("%d", &arr[i].c);
	}
}
double average(struct stu arr[n])
{
	double x = 0;
	for (int i = 0; i < n; i++)
	{
		x += arr[i].a + arr[i].b + arr[i].c;
	}
	return x / (n * 3);
}
int max(struct stu arr[n])
{
	int x = arr[0].a + arr[0].b + arr[0].c;
	int y = 0;
	for (int i = 1; i < n; i++)
	{
		if (x < arr[i].a + arr[i].b + arr[i].c)
		{
			x = arr[i].a + arr[i].b + arr[i].c;
			y = i;
		}
	}
	return y;
}
int main()
{
	struct stu arr[n];
	input(arr);
	double avera = average(arr);
	printf("总平均分:");
	printf("%lf\n", avera);
	int ma = max(arr);
	printf("最高分的学生的数据:\n");
	printf("学号:");
	printf("%d\n", arr[ma].num);
	printf("名字:");
	printf("%s\n", arr[ma].name);
	printf("科目一:");
	printf("%d\n", arr[ma].a);
	printf("科目二:");
	printf("%d\n", arr[ma].b);
	printf("科目三:");
	printf("%d\n", arr[ma].c);
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code Warrior

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值