【C语言】自定义数据类型

建议学完指针再学

为什么要有自定义数据类型?

基本数据类型不能满足我们在编程中的要求时,需要自己定义一些数据类型使用。

结构体

如果我们想存储单个数据,可以直接用数组存储;那么如果我们想存储多个数据呢?

比如我们要存储学生的学号、姓名、性别、年龄,又该如何操作呢?这就要用到结构体了

结构体的定义

struct 结构体名
{
   成员列表
};

在定义结构体时,常常会用typedef起一个别名

【C语言】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;
}

结构体变量和指针

结构体类型指针访问成员的获取和赋值形式:

  1. (*p).成员名
  2. 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呢?

原来是内存对齐惹的祸

【C语言】变量占用内存的大小&&内存对齐

共用体

成员共享同一块存储空间

共用体的定义

union 共用体名
{
   成员列表
};

定义和用法类比于结构体

共用体内存分配符合两项原则:

  1. 共用体的内存必须大于或等于其他成员变量中最大数据类型(包括基本数据类型和数组)的大小
  2. 共用体的内存必须是最宽基本数据类型的整数倍,如果不是,则填充字节

例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
};
  1. 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
  2. DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
  3. 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
  4. 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
  5. 枚举型是预处理指令#define的替代。
  6. 类型定义以分号**;**结束。

使用枚举类型对变量进行声明

方法一:枚举类型的定义和变量的声明分开

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值