C语言基础—构造类型
数据类型
1.基本类型/基础类型
-
整型
-
短整型:short[int] --2字节
-
基本整型:int --4字节
-
长整型:long[int] --32位4字节/64位8字节
-
长长整型:long long [int] (C99)
注意:以上类型又都分为signed(有符号)和unsigned(无符号)
-
-
浮点型
-
单精度:float --4字节
-
双精度:double --8字节
-
长双精度: long double --(C99新增)
-
-
字符型
-
char -- 1字节
-
2.指针类型
-
数据类型*:
int* char* float*
--8字节 -
void*:通用类型指针(万能指针)--8字节
3.空值类型
-
void:无返回值,无形参(不能定义变量)
4.构造类型(自定义类型)
-
结构体类型:struct
-
共用体/联合体类型:union
-
枚举类型:enum
结构体
结构体的定义(定义类型)
- 定义:自定义数据类型的一种,关键字struct,结构体类型的变量可以存储多个不同数据类型的数据。
- 语法:
struct 结构体名 //定义数据类型名字
{
数据类型1 成员名称1;
数据类型2 成员名称2;
...
}
注意:结构体中定义的变量,称之为成员变量
-
格式说明:
-
结构体名:合法的标识符,建议首字母大写(所谓的结构体名,就是自定义类型的类型名称)
-
数据类型n:C语言支持的所有类型(包括函数,函数在这里用函数指针表示)
-
成员名称n:合法的标识符,就是变量的命名标准
-
数据类型n成员名称n:类似于定义变量,定义了结构体中的成员
-
- 注意:结构体在定义的时候,成员不能赋值
struct Cat
{
int age = 5; //错误
double height; //正确
}
常见的定义格式
-
方式1:常规定义(命名结构体,只定义数据类型)---推荐
-
方式2:定义匿名结构体(常用于作为其他结构体的成员使用)
-
注意:定义匿名结构体的同时必须定义结构体成员,否则编译报错,结构体可以作为另一个结构体的成员(内嵌)
总结:
-
结构体可以定义在局部位置,也可以定义在全局位置(用的比较多,因为可以实现复用)
-
全局位置的结构体名和局部位置的结构体名可以相同,遵循就近原则(和变量的定义同理)
-
-
结构体类型的使用:
利用结构体类型定义变量、定义数组、也可以作为函数的返回值和参数:结构体类型的使用与基本数据类型的使用类似。
结构体变量的定义(定义变量)
三种形式定义结构体变量
结构体变量也称之为结构体实例
-
第一种:
①先定义结构体(定义数据类型)
②然后使用(使用数据类型定义变量)
-
第2种
①在定义结构体的同时,定义结构体变量
举例:
struct A { int a; char b; }x,y;
此时定义了一个结构体A,x和y是这个结构体类型的变量。
-
第3种(不推荐)
在定义匿名结构体的同时,定义结构体变量
struct A
{
int a;
char b;
struct
{
int a1;
char b1;
}c,d;
}e;
此时定义了一个没有名字的结构体(匿名结构体):c,d是这个结构体类型的变量
-
匿名结构体
-
优点:少些一个结构体名称
-
缺点:只能使用一次,定义结构体类型的同时必须定义变量
-
应用场景:
-
当结构体的类型只需要使用一次,并且定义类型的同时定义变量。
-
作为其他结构体的成员使用。
-
-
-
定义结构体的同时,定义结构体变量初始化
-
结构体成员部分初始化时,大括号{}不能省略
-
结构体成员,没有默认值,是随机值。
-
结构体变量的使用
-
结构体变量访问结构体成员
-
语法:
结构体变量名.成员名;
①可以通过访问给成员赋值(存数据)
②可以通过访问获取成员的值(取数据)
-
结构体变量未初始化,结构体的成员是随机值(和普通的变量、数组同理)
-
-
结构体变量在定义时,可以初始化
-
建议用大括号标明数据的范围
-
结构体成员初始化,可以部分初始化(和数组类以),部分初始化时一定要带大括号标明数据的范围。
-
结构体数组的定义
-
什么时候需要结构体数组
比如:我们需要管理一个学生对象,只需要定义一个struct student kelei; 假如:我们需要管理一个班的学生对象,此时就需要定义一个结构体数组struct student stus[33];
-
四种形式定义结构体数组
-
第1种:结构体→结构体变量→结构体数组
-
第2种:结构体→结构体数组,
-
第3种:结构体、结构体数组一体化(含初始化)
-
第4种:结构体、结构体数组一体化(不含初始化)
-
结构体数组的访问
语法:
结构体 -> 成员名
举例:
(*p).成员名
p -> 成员名
注意:
当
->
和[]
共存的时候,它们的优先级关系:[]
>->
构造体类型
构造体数组
举例:
#include <stdio.h>
#pragma pack(1)
struct Student
{
char *name;
int age;
float score[3];
};
#pragma pack()
void stu_print(struct Student *stu)
{
printf("学生的名字是:%s\n",stu -> name);
printf("学生的年龄是:%d\n",stu -> age);
printf("语文成绩是:%.2f\n",stu -> score[0]);
printf("数学成绩是:%.2f\n",stu -> score[1]);
printf("英语成绩是:%.2f\n",stu -> score[2]);
}
int get_avg(struct Student *stu)
{
struct Student *p = stu;
for(; p < stu + 2; p++)
{
float sum = 0;
for(int i = 0; i < 3; i ++)
{
sum += p -> score[i];
}
float avg = sum / 3;
printf("%s的平均分为:%.2f\n",p -> name, avg);
}
}
int main(int argc,char *argv[])
{
struct Student stu = {"张三",20,{90,88,75}};
stu_print(&stu);
struct Student stus[] = {
{"李四",21,{67,87,68}},
{"王五",22,{76,92,70}}
};
get_avg(stus);
return 0;
}
构造体指针
-
定义:结构体类型的指针变量指向结构体变量或者数组的起始地址。
-
语法:
struct 结构体名 *指针变量列表;
- 举例
#include <stdio.h>
struct Product
{
char *name;
int id;
int or_price;
int now_price;
char *production_deta;
char *due_deta;
};
typedef struct Product Pro;
//打印商品信息
void product_print(Pro *p)
{
Pro *t_p = p;
printf("名字是:%s\n",t_p -> name);
printf("商品ID为:%d\n",t_p -> id);
printf("原价为:%d\n",t_p -> or_price);
printf("现价为:%d\n",t_p -> now_price);
printf("生产日期:%s\n",t_p -> production_deta);
printf("到期时间是:%s\n",t_p ->due_deta);
}
int main(int argc,char *argv[])
{
//typedef struct Product Pro;
Pro p1 = {"麻辣公主",10001,100,10,"2025,1,10","2025,2,10"};
product_print(&p1);
return 0;
}
构造体成员的访问
结构体成员访问
-
结构体数组名访问结构体成员
-
语法
-
结构体数组名 -> 成员名;
(*结构体数组名).成员名;
-
结构体成员访问符
-
.
:左侧是结构体变量,也可以叫做结构体对象访问成员访问符,右侧是结构体成员。 -
->
:左侧是结构体指针,也可以叫做结构体指针访问成员符右侧是结构体成员 -
举例
-
struct Person *p = persons; // p就是结构体指针
for(; p < persons + 3; p++)
printf("%s:%d\n",p -> name, p -> count);
结构体是自定义数据类型,它是数据类型,用法类似于基本类型的 int ;结构体数组它是存放结构体对象的数组,类似于 int 数组存放 int 数据;基本类型数组怎么用,结构体数组就怎么用 ---> 可以遍历,可以作为形式参数,也可以做指针等;
结构体类型求大小
字节对齐
- 字节对齐的原因:
- 字节对齐规则
1. 默认对齐规则结构体的每个成员按其类型大小和编译器默认对齐数(通常是类型的自然对齐数)对齐。 结构体的总大小必须是最大对齐数的整数倍。2. 对齐细节基本类型的对齐数 : char ( 1 字节)、 short ( 2 字节)、 int ( 4 字节)、 double (8字节)。结构体成员的对齐 :每个成员的起始地址必须是对齐数的整数倍。结构体总大小的对齐 :结构体的总大小必须是其最大对齐数的整数倍3. #pragma pack(n) 的影响 使用 #pragma pack(n) 可以强制指定对齐数为 n ( n 为 1 、 2、 4 、 8 、 16 )。此时:每个成员的对齐数取 n 和其类型大小的较小值。结构体的总大小必须是 n 和最大对齐数中的较小值的整数倍。
共用体
union 共用体名称
{
数据类型 成员名;
数据类型 成员名;
...
}
共用体的定义和结构体类似。1.可以有名字,也可以匿名2.共用体在定义时也可以定义共用体变量3.共用体在定义时也可以初始化成员4.共用体也可以作为形参和返回值类型使用5.共用体也可以定义共用体变量...也就是说,结构体的语法,共用体都支持注意:共用体弊大于利,尽量少用,一般很少用;共用体变量在某一时刻只能存储一个数据,并且也只能取出一个数共用体所有成员共享同一内存空间,同一时间只能存储一个值,可能导致数据覆盖共用体和结构体都是自定义数据类型,用法类似于基本数据类型共用体可以是共用体的成员,也可以是结构体的成员结构体可以是结构体的成员,也可以是共用体的成员
枚举类型
建议:如果定义不相干的常量,使用宏定义(符号常量);如果需要定义一组相关的常量,如月份0~11,星期0-6,方向0-3等,使用枚举,进行统一管理。以后正式开发中,switch的case后访问的就是枚举中的常量。
-
定义
我们一般情况下,定义常量使用宏定义(#define 宏名称 值),宏定义非常适合没有关联关系的常量;但是有时候我们可能需要对一组拥有关联关系的量进行定义,如月份0~11,星期0-6,方向0-3等,那么使用宏定义,就不是很清晰,也不方便统一管理,这个时候就需要用到枚举。
枚举的存在就是将多个拥有关联关系的常量组合到一起,提高代码的可读性。
-
说明
枚举类型定义了一组常量,我们在开发中直接使用这些常量。(常用)
当然枚举类型也可以类似于结构体一样定义变量等操作。(不常用)
枚举常量有默认值,从0开始依次+1我们可以在定义时指定它的默认值,如果个别没有斌值,可以根据赋值依次+1推导。
-
特点
定义了一组常量,类似于定义了多个符号常量(宏定义)。
提高了代码的可读性。
-
语法:
-
定义枚举类型名以后就可以定义该枚举类型的变量。(枚举的成员是常量)
enum 枚举类型名 变量列表;
在定义枚举类型的同时定义该枚举类型的变量。
enum 枚举类型名(枚举元素列表) 变量列表;
直接定义枚举变量(变量)。
enum(枚举元素列表) 变量列表;
typedef
-
说明:给类型重命名,不会影响到类型本身。
-
作用:给己有的类型起别名
-
格式:
typedaef 已有类型名 新类型名;
- 举例:
#include <stdio.h>
struct Product
{
char *name;
int id;
int or_price;
int now_price;
char *production_deta;
char *due_deta;
};
typedef struct Product Pro;
//打印商品信息
void product_print(Pro *p)
{
Pro *t_p = p;
printf("名字是:%s\n",t_p -> name);
printf("商品ID为:%d\n",t_p -> id);
printf("原价为:%d\n",t_p -> or_price);
printf("现价为:%d\n",t_p -> now_price);
printf("生产日期:%s\n",t_p -> production_deta);
printf("到期时间是:%s\n",t_p ->due_deta);
}
int main(int argc,char *argv[])
{
//typedef struct Product Pro;
Pro p1 = {"麻辣公主",10001,100,10,"2025,1,10","2025,2,10"};
product_print(&p1);
return 0;
}
-
应用场景:
-
数据类型复杂(结构体、共用体、枚举、结构体指针、无符号的长整型)时使用
-
为了跨平台的兼容性,例如:
-
size_t:类型重名后的数据类型:
typedef unsigned long size_t;
-
unit16:类型重命名后的数据类型。
-