建议学完指针再学
为什么要有自定义数据类型?
基本数据类型不能满足我们在编程中的要求时,需要自己定义一些数据类型使用。
结构体
如果我们想存储单个数据,可以直接用数组存储;那么如果我们想存储多个数据呢?
比如我们要存储学生的学号、姓名、性别、年龄,又该如何操作呢?这就要用到结构体了
结构体的定义
struct 结构体名
{
成员列表
};
在定义结构体时,常常会用typedef起一个别名
实例:
#include <stdio.h>
//定义结构体Student
struct Student{
char s_id[10];
char s_name[20];
char s_sex[8];
int s_age;
};
/*typedef起别名
typedef struct Student{
char s_id[10];
char s_name[20];
char s_sex[8];
int s_age;
}ST;
定义变量时直接使用ST即可
ST stu;
*/
int main ()
{
//结构体变量的初始化
struct Student stu={"2408210","liuwen","male",18};
//结构体成员访问:使用'.'访问
printf("%s\t%s\t%s\t%d",stu.s_id,stu.s_name,stu.s_sex,stu.s_age);
return 0;
}
在C语言中不存在结构体类型的强制转换
结构体指针变量
struct Student{
char* s_id;
char* s_name;
char* s_sex;
int* s_age;
};
结构体嵌套
#include <stdio.h>
struct Date{
int year;
int month;
int day;
};
struct Student{
char s_name[20];
struct Date birthday;
float score;
};
/*也可以直接这样写
struct Student{
char s_name[20];
struct Date{
int year;
int month;
int day;
}birthday;
float score;
};
*/
int main ()
{
struct Student stu={"liuwen",2000,10,1,99.5};
printf("%s\t%d.%d.%d\t%.1f",stu.s_name,
stu.birthday.year,stu.birthday.month,stu.birthday.day,stu.score);
return 0;
}
结构体变量和指针
结构体类型指针访问成员的获取和赋值形式:
- (*p).成员名
- p->成员名
实例:
#include <stdio.h>
struct Inventory{//商品
char description[20];//货物名
int quantity;//库存数据
};
int main ()
{
struct Inventory sta={"iphone",20};
struct Inventory* stp=&sta;
printf("%s %d\n",stp->description,stp->quantity);
printf("%s %d\n",(*stp).description,(*stp).quantity);
return 0;
}
结构体和函数
#include <stdio.h>
struct School{
char s_name[20];
int s_age;
};
void Print_a(struct School sx){
printf("%s %d\n",sx.s_name,sx.s_age);
}
void Print_b(struct School* sp){
printf("%s %d\n",sp->s_name,sp->s_age);
}
int main(){
struct School sc={"xi'an",100};
Print_a(sc);
Print_b(&sc);
return 0;
}
结构体和数组
结构体数组,是指数组中的每一个元素都是一个结构体类型。在实际应用中,C语言结构体数组常被用来表示有相同的数据结构的群体,比如一个班的学生,一个公司的员工等
#include <stdio.h>
struct Student{
char s_name[20];
int age;
float score;
};
int main(){
struct Student cla[]={
{"李华",18,149.5},
{"李雷",16,130},
{"韩梅梅",16,141.5},
};
for(int i=0;i<3;i++){
printf("%s\t%d\t%f\n",cla[i].s_name,cla[i].age,cla[i].score);
}
return 0;
}
计算结构体大小
#include <stdio.h>
struct node{
char cha;
char chb;
int ia;
};
int main(){
struct node sd={'a','b',2};
printf("%d",sizeof(struct node));
return 0;
}
输出结果为:8
让我们调整一下结构体成员的顺序:
struct node{
char cha;
int ia;
char chb;
};
输出结果为:12
这是怎么一回事呢?为什么大小不是1+1+4=6呢?
原来是内存对齐惹的祸
共用体
成员共享同一块存储空间
共用体的定义
union 共用体名
{
成员列表
};
定义和用法类比于结构体
共用体内存分配符合两项原则:
- 共用体的内存必须大于或等于其他成员变量中最大数据类型(包括基本数据类型和数组)的大小
- 共用体的内存必须是最宽基本数据类型的整数倍,如果不是,则填充字节
例1.成员变量都是基本数据类型的共用体
union data{
int m;
float x;
char c;
}a;
共用体a的内存大小是最大数据类型所占的字节数,即int和float的大小,所以a的内存大小为4字节
例2.成员变量包含数组类型的共用体
union{
int m;
float x;
char c;
char str[5];
}b;
共用体b的最大数据类型为字符数组,但它的大小是5字节,不满足原则2.必须是最大基本数据类型的整数倍,所以填充3字节,共8字节
共用体变量的初始化和引用
在共用体变量的定义的同时,只能对其中一个成员的类型值进行初始化,这与它的内存分配也是响应的。
共用体变量初始化的格式如下:
union 共用体类型 共用体变量={其中一个成员的类型值};//必须用大括号括起来
完成共用体变量的初始化后,就可以引用共用体中的成员,共用体变量的引用与结构体类似,有直接引用和间接引用两种。
实例演示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Person
{
char name[20];
char role[20];
union
{
char classroom[20];
char office[20];
}dept;
}person[3];
int main() {
for (int i = 0; i < 3; i++) {
printf("please input your information:NO.%d\n", i + 1);
printf("Name:");
scanf("%s", &person[i].name);
getchar();
printf("Role:");
scanf("%s", &person[i].role);
getchar();
if (strcmp(person[i].role, "student")==0) {
printf("Classroom:");
getchar();
scanf("%s", &person[i].dept.classroom);
}
else if (strcmp(person[i].role, "teacher") == 0) {
printf("Office:");
getchar();
scanf("%s", &person[i].dept.office);
}
getchar();
}
for (int i = 0; i < 3; i++) {
printf("please input your information:NO.%3d\n", i + 1);
printf("\tName:%6s",person[i].name);
printf("\tRole:%10s",person[i].role);
if (strcmp(person[i].role, "student") == 0) {
printf("\tClassroom:%s",person[i].dept.classroom);
}
else if (strcmp(person[i].role, "teacher") == 0) {
printf("\tOffice:%6s",person[i].dept.office);
}
printf("\n");
}
}
枚举类型
在程序中,可能需要为某些整数定义一个别名,我们可以利用预处理指令#define来完成这项工作,您的代码可能是:
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
在此,我们定义一种新的数据类型,希望它能完成同样的工作。这种新的数据类型叫枚举型。
枚举类型的定义
格式:
enum 枚举类型名
{
成员列表
};
示例:
enum DAY
{
MON = 1 , TUE, WED, THU, FRI, SAT, SUN
};
- 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
- DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
- 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
- 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
- 枚举型是预处理指令#define的替代。
- 类型定义以分号**;**结束。
使用枚举类型对变量进行声明
方法一:枚举类型的定义和变量的声明分开
enum DAY
{
MON = 1 , TUE, WED, THU, FRI, SAT, SUN
};
enum DAY yesterday;
enum DAY today;
enum DAY tomorrow; // 变量tomorrow的类型为枚举型enum DAY
enum DAY good_day, bad_day; // 变量good_day和bad_day的类型均为枚举型enum DAY
方法二:类型定义与变量声明同时进行:
enum //跟第一个定义不同的是,此处的 标号DAY省略,这是允许的。
{
saturday,
sunday = 0 ,
monday,
tuesday,
wednesday,
thursday,
friday
} workday; // 变量workday的类型为枚举型enum DAY
enum week { Mon = 1 , Tue, Wed, Thu, Fri Sat, Sun} days; // 变量days的类型为枚举型enum week
enum BOOLEAN { false , true } end_flag, match_flag; // 定义枚举类型并声明了两个枚举型变量
方法三:用typedef关键字将枚举类型定义成别名,并利用该别名进行变量声明:
typedef enum workday
//enum workday中的workday可以省略
{
saturday,
sunday = 0 ,
monday,
tuesday,
wednesday,
thursday,
friday
} workday; // 此处的workday为枚举型enum workday的别名
workday today, tomorrow; // 变量today和tomorrow的类型为枚举型workday,也即enum workday
注意:同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的命名常量。错误示例如下所示:
错误声明一:存在同名的枚举类型
typedef enum
{
wednesday,
thursday,
friday
} workday;
typedef enum WEEK
{
saturday,
sunday = 0 ,
monday,
} workday;
错误声明二:存在同名的枚举成员
typedef enum
{
wednesday,
thursday,
friday
} workday_1;
typedef enum WEEK
{
wednesday,
sunday = 0 ,
monday,
} workday_2;
对枚举型的变量赋整数值时,需要进行类型转换
#include<stdio.h>
enum DAY { MON = 1 , TUE, WED, THU, FRI, SAT, SUN };
int main()
{
enum DAY yesterday, today, tomorrow;
yesterday = TUE;
today = ( enum DAY) (yesterday + 1 ); // 类型转换
tomorrow = ( enum DAY) 30 ; // 类型转换
// tomorrow = 30; // 错误
printf( " %d %d %d \n " , yesterday, today, tomorrow); // 输出:2 3 30
}
使用枚举型变量
#include<stdio.h>
enum
{
BELL = '\a' ,
BACKSPACE = '\b' ,
HTAB = '\t' ,
RETURN = '\r' ,
NEWLINE = '\n' ,
VTAB = '\v' ,
SPACE = ' '
};
enum BOOLEAN { FALSE = 0 , TRUE } match_flag;
int main()
{
int index = 0 ;
int count_of_letter= 0 ;
int count_of_space = 0 ;
char str[] = "I'm Ely efod" ;
match_flag = FALSE;
for (; str[index] != '\0' ; index ++ )
if ( SPACE != str[index] )
count_of_letter ++ ;
else
{
match_flag = ( enum BOOLEAN) 1 ;
count_of_space ++ ;
}
printf( "%s %d times %c" , match_flag ? "match" : "not match" , count_of_space, NEWLINE);
printf( "count of letters: %d %c%c " , count_of_letter, NEWLINE, RETURN);
}
运行结果:
match 2 times
count of letters: 10
枚举类型与sizeof运算符
#include<stdio.h>
enum escapes
{
BELL = '\a' ,
BACKSPACE = '\b' ,
HTAB = '\t' ,
RETURN = '\r' ,
NEWLINE = '\n' ,
VTAB = '\v' ,
SPACE = ' '
};
enum BOOLEAN { FALSE = 0 , TRUE } match_flag;
int main()
{
printf( "%d bytes \n" , sizeof (enum escapes)); // 4 bytes
printf( "%d bytes \n" , sizeof (escapes)); // 4 bytes
printf( "%d bytes \n" , sizeof (enum BOOLEAN)); // 4 bytes
printf( "%d bytes \n" , sizeof (BOOLEAN)); // 4 bytes
printf( "%d bytes \n" , sizeof (match_flag)); // 4 bytes
printf( "%d bytes \n" , sizeof (SPACE)); // 4 bytes
printf( "%d bytes \n" , sizeof (NEWLINE)); // 4 bytes
printf( "%d bytes \n" , sizeof (FALSE)); // 4 bytes
printf( "%d bytes \n" , sizeof ( 0 )); // 4 bytes
}
参考博文:
https://blog.csdn.net/weixin_48560325/article/details/124280883
https://blog.csdn.net/Jacky_Feng/article/details/109219560