c语言结构体学习整理(结构体的初始化,自引用和偏移)

1.结构体的介绍

c语言结构体(struct)从本质上讲是一种自定义的数据类型,只不过这些数据比较复杂,是由 int ,char,float等基本类型组成的。你可以认为结 构体是一种聚合类型。

在实际开发中,我们可以将一组类型不同的,但是用来描述用一件事物的变量放到结构体中。例如,在校学生有姓名,年龄,身高,成绩等属性,学了结构体后,我们就不需要再定义多个变量了,将他们都放到结构体中即可。

2.结构体的声明和初始化

#define _CRT_SECURE_NO_WARNINGS 1
#include

//声明一个学生的结构体类型

struct stu//stu是学学生的意思,这个可以改变
{

	char name[20];//这里面是成员列表
	int age;
	float score;


}s1,s2;//si,s2是2个结构体变量,是全局的


int main()
{
	int a = 0;
	int b = 0;

	struct stu s = {"zhangsan",20,95.5f};//定义一个结构体变量,局部的
	printf(" %s %d %f\n",s.name, s.age, s.score);//使用.访问struct stu结构体里面的成员

	return 0;
}

3.结构体的传参和打印

#define _CRT_SECURE_NO_WARNINGS 1
#include






struct s
{
	int arr[1000];
	float f;
	char ch[100];

	
};
//这个方式的传参比较占内存
void print1(struct s tmp)
{
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		printf("%d ", tmp.arr[i]);

	}
	printf("\n");
	printf("%f\n", tmp.f);
	printf("%s\n", tmp.ch);

}


//指针传参只拷贝第一个地址不占内存
void print2(struct s* sum)
{
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		printf("%d ", sum->arr[i]);
	
	}
		printf("\n");
		printf("%f\n", sum->f);
	    printf("%s\n", sum->ch);
	


}

int main()
{
	struct s s = { {1,2,3,4,5,6,7,8,9,10},5.5f,"hello nihao" };

	print1(s);//传结构体
	print2(&s);//这里必须传地址
	return 0;
}

//函数传参的时候,参数是需要压栈的。
//如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的
//下降。



上面是结构体最基本的使用。

4.匿名结构体



//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;//匿名结构体的变量只能在这里创建补能在其他地方创建所以匿名结构体只能使用一次。

5.结构的自引用

自己类型的对象找自己类型对象

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第1张图片

 




struct Node
{

    int data;//这里放入一个数据
    struct Node* next;  这里的意思是一个节点包含下一个节点


};


//这里的Node没人认识,如果对这个结构体重命名的时候这个类型是清晰可见的必须是存在的。
typedef struct
{
int data;
Node* next;
}Node;

上面的代码





//解决方案:
typedef struct Node
{
int data;
struct Node* next;
}Node;





typedef在结构体定义中的应用

        typedef是c语言关键字,作用是为一种数据类型定义一个新的名字。对于以上两种结构体定义形式,typedef都可以为其创建别名。

        (1) 为结构体A创建一个别名tagA

          typedef struct A
          {undefined

              ...                           

           }tagA;


        (2) 为匿名结构体创建一个别名tagA

           typedef struct

           {undefined

                 ...                           

            }tagA;

          typedef在这里的应用解决了匿名结构体不能被再引用的问题。


 三、总结

        不管是以上那种方式的应用,在c语言中使用typedef帮助定义结构体,对于声明结构体变量,便可以直接使用tagA xxx(结构体名 对象名)的形式,而没必要带上struct关键词。实际项目C代码中,结构体的定义一般都使用了typedef,在大量使用结构体变量的场合,这样会省事很多。


6.结构体内存对齐

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第2张图片

下面是嵌套结构体的情况

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第3张图片

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第4张图片

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第5张图片

c语言结构体学习整理(结构体的初始化,自引用和偏移)_第6张图片

 

 

 

 

 结构体对齐的规则:

1.第一个成员在与结构体变量偏移量为0的地址处。

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

对齐数=编辑器默认的一个对齐数与该成员大小的较小值。

                   vs中默认的值是8

                    linux里面没有默认对齐数对齐数就是成员自身大小。

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍数。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

offsetof函数:

1.计算结构体成员的偏移。头文件是stddef.h

为什么要存在内存对齐?

1.平台原因(移植原因);

不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(如果在某些特定位置取我们的值的话,存这些值的时候也要存在特定的位置。比如说我们只能在那些对齐的位置上取数据那我们要把它对齐到那个数据上在存数据)。

2.性能原因:

数据结构体(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作俩次内存访问;而对齐的内存访问仅需要一次访问。

总体来说的内存对齐是拿空间来换取时间的做法。

你可能感兴趣的:(c语言)