数据类型
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:类型重命名后的数据类型。
-