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