C语言支持自定义数据类型,C语言中的自定义数据类型使用多个已有的基本数据类型组合构成新的数据类型
自定义数据类型有以下三种语法:
结构体
联合(共用体)
枚举
typedef是一个关键字,用于给一个已经存在的数据类型起别名(不能使用宏定义给类型起别名)
语法:
typedef 类型名 类型别名;//写在函数外面
注:在之后的代码中,可以使用类型别名当类型名来使用,他们完全等价
例:
typedef int T;
int main()
{
T num=10;
}
使用typedef为类型起别名可以提高代码的可移植性(不同平台的数据类型长度可能不同,使用typedef可以很方便修改类型)
typedef也可以给函数指针起别名,语法就是在函数指针声明前面加typedef,这样原来的变量名就变成了类型别名
例:
typedef int (*pfunc_t)(int,int);
//pfunc_t是int (*)(int,int)的别名
pfunc_t p_func = add;
p_func(1,2);
结构体用来给C语言增加新的数据类型,新的数据类型是由多个已知的数据类型构成的
结构体本身代表的是类型,不占用内存空间
结构体必须先声明才可以使用,声明结构体使用struct关键字
结构体声明语句应该写在函数之外,通常写在头文件中
struct 结构体类型名
{
子变量的声明;//子变量的声明不能初始化
......
};
使用结构体类型声明结构体变量时必须加上struct关键字
struct 结构体类型名 结构体变量;
struct 结构体类型名
{
子变量的声明;//子变量的声明不能初始化
......
} 结构体变量名;
typedef struct 结构体类型名
{
子变量的声明;//子变量的声明不能初始化
......
} 结构体类型别名;
注:类型别名和类型名可以同名
声明结构体不提供类型名,这种语法叫匿名结构体
struct
{
子变量的声明;//子变量的声明不能初始化
......
} 结构体变量名;
注:这种语法不常用
可以使用typedef给匿名结构体起别名
typedef struct
{
子变量的声明;//子变量的声明不能初始化
......
} 结构体类型别名;
结构体类型别名 结构体变量名;
可以使用初始化数组的语法来初始化结构体变量
同类型的结构体变量可以直接赋值(采用结构体内存拷贝的方式,而不是逐个成员的拷贝)
C语言中结构体不能是函数,但是可以是函数指针
通过结构体变量访问结构体成员使用 成员运算符(.)
语法:
结构体变量名.结构体成员变量名;
结构体指针用于指向结构体变量,声明语法就是声明指针的语法
struct 指向结构体类型 *指针变量名;
可以通过结构体指针来访问结构体变量的成员,使用箭头运算符(->)
语法:
结构体指针->结构体成员变量名;
结构体变量可以作为函数的参数(形参和实参是占用不同的存储单元,是采用传值的方式)
例:
struct Student
{
char ID[7];
char name[10];
char sex;
int age;
}
void inputStudent(struct Student s)
{
scanf("%s",s.ID);
scanf("%s",s.name);
scanf("%s",&s.sex);
scanf("%d",&s.age);
}
在实际开发中,不使用结构体变量作为函数的参数和返回值,而应该使用结构体指针(传递的是结构体变量的首地址)
使用结构体指针可以节省函数调用时的空间消耗,同时也实现数据双向传递
例:
void inputStudent(struct Student *s)
{
scanf("%s",s->ID);
scanf("%s",s->name);
scanf("%s",&s->sex);
scanf("%d",&s->age);
}
结构体变量可以作为函数的参数和返回值
例:
struct Student inputStudent()
{
struct Student s
scanf("%s",s.ID);
scanf("%s",s.name);
scanf("%s",&s.sex);
scanf("%d",&s.age);
return s;
}
计算机中所有类型数据在存储时必须满足存储地址是自身大小的整数倍
double和long long也是按4字节对齐
结构体内部的成员也必须数据对齐,容易造成结构体内部的成员之间有空隙,结构体的大小很可能不是所有成员大小之和
结构体对齐规则:
一个结构体变量一定是有多个基本数据类型的成员组成,假设所占空间最大的成员大小为n,整个结构体的大小必须是n的整数倍(n超过4只需要是4的整数倍)
假设结构体变量首地址为0,第一个成员自然对齐,结构体每个成员的地址必须是自身大小的整数倍(超过4只需要是4的整数倍)
注:32位系统最大按4字节对齐,64位系统最大按8字节对齐
在编程中可以强制指定程序中数据对齐方式,使用以下预处理指令来实现:
#pragma pack(n) ------------ 以下代码按最大按n字节对齐
结构体体中的成员也可以是结构体
例:
struct Date //日期类型
{
int year;
int month;
int day;
};
struct Student
{
char ID[7];
char name[10];
char sex;
struct Date birthday; //出生日期
}s1;
struct Student s2={"160503","Smith",'M',{1997,8,9}} //成员初始化
struct Student s2={"160503","Smith",'M',1997,8,9}
s2.birthday.year=1998; //成员的访问
共用体是将不同类型的一组数据组织为一个整体,共享一个存储单元
语法:
union 共用体名称
{
数据类型 成员名1;
数据类型 成员名1;
.......
数据类型 成员名n;
};
定义方法与结构体基本相识
例:
union number
{
int n;
float f;
char c;
}
整个共用体所占的内存单元的大小实际上是最宽基本数据类型的整数倍
例:
union number
{
int n; //4个字节
float f; //4个字节
char c; //1个字节
} //整体占最大类型float的整数倍,4个字节
枚举:将所有可能的值一一列举出来
语法:
enum 枚举类型名 {枚举常量1,枚举常量2,......};
每个枚举常量都有一个值,依次从0到n
枚举常量的值也可由用户自己定义
例:
enum Color{red=2,orange,yellow=6,green,blue};
语法:
enum 枚举类型名 {枚举常量1,枚举常量2,......} 枚举变量;
例:
enum Color{red,orange,yellow,green,blue} yanse;
语法:
enum 枚举类型名 {枚举常量1,枚举常量2,......};
enum 枚举类型名 枚举变量;
例:
enum Color{red,orange,yellow,green,blue};
enum Color yanse;
语法:
enum {枚举常量1,枚举常量2,......} 枚举变量;
例:
enum {red,orange,yellow,green,blue} yanse;
枚举常量不能对枚举元素赋值
枚举变量的取值范围只能是枚举类型所列举的常量。通常将枚举常量赋值给枚举变量,如果直接将整数赋值给枚举变量,部分编译器编译会报错,需要进行强制转换
例:
enum Color{red,orange,yellow,green,blue} yanse;
yanse=red;
yanse=(enum Color)1;
枚举类型是被当做int类型输入输出的
例:
printf("%d",yanse);
C语言的函数是有参数,一共有三个参数,形式如下:
int main(int argc,char *argv[],char *env[]);
参数:
argc:命令行参数个数(包括可执行程序本身)
argv:命令行参数内容
env: 环境变量的内容
目前只关注前两个参数:
int main(int argc,char *argv[]);
动态分配的内存存放在堆中,动态内存由程序员管理(由自己申请,自己释放),动态内存分配的是连续空间
使用动态内存需要用到C语言提供的标准函数 malloc/free,malloc用于申请,free用于释放,使用这两个函数需要包含stdlib.h
由于动态内存由程序员管理,所以申请和释放要一一对应,否则会造成内存泄漏
#include
void *malloc(size_t size);
void free(void *ptr);
malloc:
参数:
size:要申请的动态内存的大小(字节)
返回值:
成功返回申请空间的首地址,失败返回NULL
free:
参数:
ptr:要释放动态内存首地址,就是malloc的返回值
例:
p = malloc(xxxx);
//使用动态内存
free(p);
动态内存使用前先申请,使用完要释放