C语言结构体相关知识

本文的编译环境为 Visual Studio 2022

何为结构体?

结构体是一种自定义类型,是多种数据类型的集合。它和数组很像,不过数组里面是存储单一类型的成员变量集合,而结构体里面可以放整形,浮点型,字符型,数组,指针等一系列不同数据类型的成员变量。
struct是定义结构体类型的关键字,下面这个就是结构体的基本形式:

struct S//(结构体名字可以自己定义)
{
  //成员列表
	int age;
	char name[20] ;
	float score;
	short class; 
}student;//变量列表

结构体变量声明

全局声明

声明一个全局的结构体变量 student,全局有效。

struct S
{
	int age;
	char name[20];
	float score;
	short class;
}student;

int main()
{
	return 0;
}

局部声明

声明一个局部的结构体变量 student,只在mian函数里有效。

int main()
{
	struct S
	{
		int age;
		char name[20];
		float score;
		int class;
	}student;
	return 0;
}

隐式声明

结构体的隐式声明就是把结构体的名字给省略不写,但造成的后果就是结构体只能声明一次。

struct //省略了结构体名
{
	int age;
	char name[20];
	float score;
	int class;
}student;

int main()
{
	return 0;
}

结构体重命名

结构体因为也是一种数据类型,所以自然也可以使用typedef,对其进行重命名,此时变量列表这里的student就变成了struct S的另一个名字,而不再是结构体变量。

typedef struct S
{
	int age;
	char name[20];
	float score;
	int class;
}student;

int main()
{
	return 0;
}

大多的情况下为了简洁就把会把结构体变量名给省略,这样也是可行的。

typedef struct 
{
	int age;
	char name[20];
	float score;
	int class;
}student;

int main()
{
	return 0;
}

结构体变量的四种初始化方式

一:

struct  S
{
	int age;
	char name[20];
	float score;
	int class;
}student = {10,"lisi",66.5,208};
int main()
{
	return 0;
}

二:

struct  S
{
	int age;
	char name[20];
	float score;
	int class;
};
int main()
{
	struct  S student ={10,"lisi",66.5,208};
	return 0;
}

三:

struct  S
{
	int age;
	char name[20];
	float score;
	int class;
};
int main()
{
	struct  S student ={.age=10,.score=66.5,.name="lisi",.class=208};
	return 0;
}

四:

struct  S
{
	int age;
	char name[20];
	float score;
	int class;
};
int main()
{
	struct  S student;
	student.score = 66.5;
	student.class = 208;
	student.age = 10;
	student.name[20] ="lisi";
	return 0;
}

结构成员访问操作符

结构体变量.成员变量名
结构体指针—>成员变量名

#include <stdio.h>
#include <string.h>
struct  S
{
	int age;
	char name[20];
	float score;
	int class;
} ;
int main()
{
	struct  S student;
	struct  S *p=&student;
	p->age = 10;
	p->class = 208;
	//p->name = "lisi";//不能这样赋值
	strcpy(p->name, "lisi");
	p->score = 66.5;
	printf("%s %d\n", p->name, p->class);
	strcpy(student.name, "zhangsan");
	student.class = 315;
	printf("%s %d\n", student.name, student.class);
	return 0;
}

结构体自引用

结构体的变量列表里,可以存放各种数据类型的成员变量,那是不是也可以存放结构体变量呢,答案是肯定的,不过有一定的注意事项。

struct  S
{
	int age;
	struct S next;
}student  ;

上面的这段自引用代码是不行的在编译的过程中会保错,因为无法求的这个结构体所占内存的大小。

struct  S
{
	int age;
	struct S *next;
}student  ;

这样是可以的,指针的大小是固定的它只占4或8个字节。

结构体传参

#include <stdio.h>
struct  S
{
	int height;
	int age[100];
};
//传值
void print1(struct S ps)
{
	printf("%d\n", ps.height);
}
//传址
void print2(struct S* ps)
{
	printf("%d\n", ps->height);
}

int main()
{
	struct  S man = {175,{10,23,15,656}};
	print1(man);//传值
	print2(&man);//传址
	return 0;
}

在结构体传参的时候推荐首选传址,因为传参的话,参数是会进行压栈操作的,会有一定的时间和空间的开销,要是传递的结构体过大,会导致参数在压栈是的系统开销较大,会导致系统性能下降。

结构体内存对齐

在学会,怎么使用结构图后,我们还要学会怎么计算结构体的⼤⼩,这也是一个重要的知识点。

对齐规则

1: 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
2: 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
VS中默认的值为8
3.:结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
4.:如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

内存对齐的意义

在C语言中存在内存对齐,主要是为了满足硬件的要求和提高数据访问的效率。具体来说,C语言中存在内存对齐的原因包括以下几点:

  1. 硬件要求:一些处理器对于特定类型的指令或数据有对齐的要求。未对齐的数据访问可能会导致硬件异常或错误。为了保证程序在不同的硬件平台上都能正常运行,C语言引入了内存对齐规则,使得数据满足硬件的对齐要求。
  2. 访问效率:现代计算机体系结构通常以字(word)为基本单位进行数据的读取和写入。当数据在内存中对齐时,CPU 可以一次性读取或写入与字宽度相符合的数据,这样可以提高数据访问的效率。相反,如果数据未对齐,CPU 可能需要多次访问内存来获取或写入数据,这降低了访问效率。
  3. 数据结构组织:在C语言中,我们可以使用结构体(struct)或联合体(union)来组织数据。为了提高存储空间的利用率和访问效率,C编译器会在结构体或联合体的成员之间插入填充字节,使得每个成员的起始地址都满足特定的对齐要求。
    总而言之,C语言中存在内存对齐的目的是为了满足硬件要求,提高数据访问效率和存储空间的利用率。编译器会根据特定的对齐规则对数据进行对齐处理,以便在不同的硬件平台上保证程序的正确性和性能。