结构体对齐

Posted Truman001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构体对齐相关的知识,希望对你有一定的参考价值。

1 C语言里可以在一个存储区里记录多个相关数字这种存储区的类型叫结构体类型,这种类型需要首先创建出来然后才能使用

2 结构体类型存储区里包含多个子存储区,每个子存储区可以记录一个数字,结构体中不同子存储区的类型可以不同,子存储区甚至也可以是结构体类型的存储区

3 结构体声明语句可以用来创建结构体类型,结构体声明语句中需要使用struct关键字

4 结构体声明语句中包含多个变量声明语句,每个变量叫做结构体的成员变量,C语言中结构体不可以包含函数

5 成员变量声明语句不会分配内存,它只是用来表示子存储区的类型和名称的,结构体声明语句不分配内存所以可以写在任何地方(包括头文件里)

6 结构体类型可以用来声明变量,这种变量叫结构体变量,结构体变量真正被分配了内存,它才可以用来记录数字

7 把struct关键字和结构体名称合起来作为类型名称就可以声明结构体变量了

8 可以使用typedef关键字给数据类型起别名,别名可以用来代替原来的类型名称,可以把声明结构体的语句和起别名的语句,合并成一条语句,这个时候可以省略结构体本身的名称,但是以后就只能用别名声明结构体变量了

9 结构体变量应该进行初始化,结构体变量初始化的格式和数组初始化的格式一样

10 通常不会把结构体存储区当作整体使用,一般一次只使用其中某个子存储区,可以在结构体变量名称后使用.然后再加上某个成员变量名称,这个写法可以表示某个子存储区

11 同类型结构体变量之间可以直接赋值

12 结构体指针可以用来记录结构体存储区的地址,当一个结构体指针和一个结构体存储区捆绑后就可以在指针后使用->然后再加上成员变量名称来表示某个子存储区

#include <stdio.h>
/*struct person {
	int age;
	float height;
	char name[10];
};
typedef struct person sperson;*/
typedef struct /*person*/ {
	int age;
	float height;
	char name[10];
} sperson;
int main() 
{
	//struct person prsn;  //结构体变量声明语句
	sperson prsn1 = {20, 1.74f, "abc"};
	sperson prsn2 = {0};
	sperson *p_person = NULL;  //结构体指针
	printf("请输入年龄:");
	scanf("%d", &(prsn1.age));
	printf("请输入身高:");
	scanf("%g", &(prsn1.height));
        scanf("%*[^\n]");
	scanf("%*c");
	printf("请输入姓名:");
	fgets(prsn1.name, 10, stdin);
	printf("年龄是%d\n", prsn1.age);
	printf("身高是%g\n", prsn1.height);
	printf("姓名是%s\n", prsn1.name);
	prsn2 = prsn1;
	printf("年龄是%d\n", prsn2.age);
	printf("身高是%g\n", prsn2.height);
	printf("姓名是%s\n", prsn2.name);
	p_person = &prsn1;
	printf("年龄是%d\n", p_person->age);
	printf("身高是%g\n", p_person->height);
	printf("姓名是%s\n", p_person->name);
	return 0;
}

  

#include <stdio.h>
typedef struct {
	int row, col;
} pt;
typedef struct {
	pt pt1, pt2;
} rect;
int main() {
	rect r1 = {0}, *p_rect = NULL;
	pt mid = {0}, *p_mid = NULL;
	/*printf("请输入水平长方形的位置:");
	scanf("%d%d%d%d", &(r1.pt1.row), &(r1.pt1.col), &(r1.pt2.row), &(r1.pt2.col));
	mid.row = (r1.pt1.row + r1.pt2.row) / 2;
	mid.col = (r1.pt1.col + r1.pt2.col) / 2;
	printf("中间点位置是(%d, %d)\n", mid.row, mid.col);*/
	p_rect = &r1;
	p_mid = ∣
	printf("请输入水平长方形的位置:");
	scanf("%d%d%d%d", &(p_rect->pt1.row), &(p_rect->pt1.col), &(p_rect->pt2.row), &(p_rect->pt2.col));
	p_mid->row = (p_rect->pt1.row + p_rect->pt2.row) / 2;
	p_mid->col = (p_rect->pt1.col + p_rect->pt2.col) / 2;
	printf("中间点位置是(%d, %d)\n", p_mid->row, p_mid->col);
	return 0;
}

  

13 结构体类型形式参数可以把结构体数据从调用函数传递给被调用函数,这种做法会导致时间和空间上的浪费,使用结构体指针形式参数可以避免这种浪费

#include <stdio.h>
typedef struct {
	int row, col;
} pt;
pt *read(pt *p_pt) {
	pt pt1 = {0};
	printf("请输入点的位置:");
	scanf("%d%d", &(p_pt->row), &(p_pt->col));
	return p_pt;
}
void print(const pt *p_pt) {
	printf("点的位置是(%d, %d)\n", p_pt->row, p_pt->col);
}
int main() {
	pt pt1 = {0}, *p_pt = NULL;
	p_pt = read(&pt1);
	print(p_pt);
	return 0;
}

  

14 声明结构体指针形式参数的时候尽量使用const关键字

#include <stdio.h>
typedef struct 
{   int row, col; } pt; typedef struct
{   pt pt1, pt2; } rect; int area(const rect *p_rect)
{ int ret = (p_rect->pt1.row - p_rect->pt2.row) * (p_rect->pt1.col - p_rect->pt2.col);   return ret >= 0 ? ret : 0 - ret; } int main()
{   rect r1 = {0}; printf("请输入一个水平长方形的位置:"); scanf("%d%d%d%d", &(r1.pt1.row), &(r1.pt1.col), &(r1.pt2.row), &(r1.pt2.col)); printf("面积是%d\n", area(&r1)); return 0; }

  

15 可以把结构体变量作为返回值使用把结构体数据传递给调用函数,这样也会造成时间和空间的浪费,把结构体存储区的地址作为返回值可以避免这种浪费

#include <stdio.h>
typedef struct {
	int row, col;
} pt;
typedef struct {
	pt center;
	int radius;
} circle;
circle *larger(const circle *p_cl1, const circle *p_cl2) {
	eturn (circle *)(p_cl1->radius > p_cl2->radius ? p_cl1 : p_cl2);
}
int main() {
	circle cl1 = {0}, cl2 = {0}, *p_circle = NULL;
	printf("请输入一个圆的位置:");
	scanf("%d%d%d", &(cl1.center.row), &(cl1.center.col), &(cl1.radius));
	printf("请再次输入一个圆的位置:");
	scanf("%d%d%d", &(cl2.center.row), &(cl2.center.col), &(cl2.radius));
	p_circle = larger(&cl1, &cl2);
	printf("结果是((%d, %d), %d)\n", p_circle->center.row, p_circle->center.col, p_circle->radius);
	return 0;
}

  

16 不可以把局部结构体存储区的地址作为返回值使用

17 一个存储区的地址必须是它自身大小的整数倍(double类型存储区的地址只需要是4的整数倍),这个规则叫数据对齐,结构体内部的子存储区通常也需要遵守数据对齐的规则

  数据对齐可能导致结构体不同子存储区之间有空隙

18 结构体存储区的大小必须是内部包含的最大基本类型子存储区大小的整数倍(如果最大基本类型子存储区的类型是double则结构体存储区的大小只需要是4的整数倍)这个规则叫数据补齐,数据补齐可能造成结构体存储区最后有浪费的字节

#include <stdio.h>
typedef struct {
	char buf[2];
	int num;
} tmp;
typedef struct {
	char ch;
	int num;
	char ch1;
} tmp1;
int main() {
	printf("sizeof(tmp)是%d\n", sizeof(tmp));	//8
	printf("sizeof(tmp1)是%d\n", sizeof(tmp1));   //12
	return 0;
}

 19 总结

#include <stdio.h>
typedef struct {
	int row, col;
} pt;
typedef struct {
	pt center;
	int radius;
} circle;
int len2(const pt *p_pt1, const pt *p_pt2) 
{ return (p_pt1->row - p_pt2->row) * (p_pt1->row - p_pt2->row) + (p_pt1->col - p_pt2->col) * (p_pt1->col - p_pt2->col); } int relation(const pt *p_pt, const circle *p_cl)
{ int tmp = len2(p_pt, &(p_cl->center));    if (tmp > p_cl->radius * p_cl->radius)
     { return 1; } else if (tmp < p_cl->radius * p_cl->radius)
     { return -1; } else
     { return 0; } } int main()
{ int tmp = 0; pt pt1 = {0}; circle cl = {0}; printf("请输入一个点的位置:"); scanf("%d%d", &(pt1.row), &(pt1.col)); printf("请输入一个圆的位置:"); scanf("%d%d%d", &(cl.center.row), &(cl.center.col), &(cl.radius)); tmp = relation(&pt1, &cl); if (tmp == 1) { printf("点在圆外\n"); } else if (!tmp) { printf("点在圆上\n"); } else { printf("点在圆内\n"); } return 0; }

  

以上是关于结构体对齐的主要内容,如果未能解决你的问题,请参考以下文章

结构体字节对齐问题探究

[.NET] 结构体布局详解与结构体内存对齐具体方式

[.NET] 结构体布局详解与结构体内存对齐具体方式

C语言结构体字节对齐

c语言结构体字节对齐详解

关于字节对齐的理解