C语言——结构体、typedef、联合体(共用体)、枚举类型

时间:2022-09-05 18:03:41

一.结构体

作用:组合不同类型的数据。

声明语法:

struct 结构体名称    //结构体名称通常第一个名称为大写。
{
   结构体成员1;
   结构体成员2;
   结构体成员3;
   结构体成员4;
};

定义语法:

struct 结构体名称 结构体变量名;

也可以在声明时定义,看下后例子

 访问结构体变量:点号运算符(.)

例:

//书籍录入系统
#include <stdio.h>

struct Book		//声明有这样一个结构体
{
	char title[128];
	char author[40];
	float price;
	unsigned int date;
	char publisher[40];
} book1;//定义个全局变量的结构体book1

int main(void)
{
	struct Book book;//定义局部变量结构体book

	printf("请输入书名:");
	scanf("%s", book.title);//访问结构体变量
	getchar();
	printf("请输入作者:");
	scanf("%s", book.author);
	getchar();
	printf("请输入售价:");
	scanf("%f", &book.price);		//字符串不用取地址,而数需要&
	getchar();
	printf("请输入出版日期:");
	scanf("%d", &book.date);
	getchar();
	printf("请输入出版社:");
	scanf("%s", book.publisher);
	getchar();

	printf("=========数据录入完毕==========\n");
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("售价:%.2f\n", book.price);
	printf("出版日期:%u\n", book.date); 
	printf("出版社:%s\n", book.publisher);

	getchar();	//putchar()输出单个字符、getchar()过滤回车符
	return 0;
}

初始化结构体变量:(注意结构体的成员类型要一致)

struct Book book =
{
	"《C Primer Plus》",
	"Stephen P rata",
	60.00,
	20052,
	"人民邮电出版社"
};

初始化结构体指定成员值:(也可以在定义时初始化。)

struct Book book ={.price = 60.00}

注:这样写可以不按结构体声明的成员顺序初始化。

####编译器会对结构体成员进行内存对齐####微调节省空间

例:

#include <stdio.h>

struct A
{
	char a;
	int b;
	char c;
} a = {'x', 520, 'o'};//定义并初始化

int main(void)
{
	printf("size of a = %d\n", sizeof(a));
	
	getchar();
	return 0;
}

结果:

size of a = 12

微调:

#include <stdio.h>

struct A
{
	char a;
	char c;
	int b;
} a = {'x', 'o', 520};//定义并初始化

int main(void)
{
	printf("size of a = %d\n", sizeof(a));
	
	getchar();
	return 0;
}

结果:

size of a = 8

图解:

C语言——结构体、typedef、联合体(共用体)、枚举类型


结构体嵌套:

例:

//书籍系统
#include <stdio.h>

struct Date
{
	int year;
	int month;
	int day;
}date;

struct Book		//声明有这样一个结构体
{
	char title[128];
	char author[40];
	float price;
	struct Date date;//结构体嵌套
	char publisher[40];
} book = {
	"《C Primer Plus》",
	"Stephen P rata",
	60.00,
	{2005, 2, 13},//嵌套赋值
	"人民邮电出版社"
};

int main(void)
{
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("售价:%.2f\n", book.price);
	printf("出版日期:%u-%u-%u\n", book.date.year, book.date.month, book.date.day);//嵌套访问方法
	printf("出版社:%s\n", book.publisher);

	getchar();	
	return 0;
}

结构体数组:(注:每个数组是一个结构体数据)

结构体数组定义和结构体定义相识

//一//
struct 结构体名称
{
	结构体成员;
} 数组名[长度];
/////////////////
//二//
struct 结构体名称
{
	结构体成员;
};
struct 结构体名称 数组名[长度];     

初始化类似。


结构体指针:

如:struct Book *pt;//直接定义结构体指针

要使结构体指针指向结构体

pt = &book;//取值一定要用取值运算符&,因为结构体指针与数组不一样。结构体名不是指向成员第一个。

结构体指针访问结构体成员:

结构体指针->成员名

结构体变量作为函数的参数:(注:变量传入函数的方式有两种:传值与传址)

例:

//图书管理系统
#include<stdio.h>
#include<stdlib.h>

void getInput(struct Book );//getInput函数声明
void printBook(struct Book );//printBook函数声明

struct Date	//出版时间结构体
{
	int year;//年
	int month;//月
	int day;//日
};

struct Book	//book结构体
{
	char title[128];//书名
	char author[40];//作者
	float price;//售价
	struct Date date;//出售时间
	char publisher[40];//出版社
};

void getInput(struct Book book)	//返回结构体的函数参数也是结构体<是一个函数>
{
	printf("请输入书名:");
	scanf("%s", book.title);
	getchar();
	printf("请输入作者:");
	scanf("%s", book.author);
	getchar();
	printf("请输入售价:");
	scanf("%f", &(book.price));
	getchar();
	//book->date = (struct Date)malloc(sizeof(struct Date));
	printf("请输入日期(2015-1-24):");
	scanf("%d-%d-%d", &(book.date.year), &(book.date.month), &(book.date.day));
	getchar();
	printf("请输入出版社:");
	scanf("%s", book.publisher);
	getchar();

	//return 0;//赋值完毕,传出结构体。
}

void printBook(struct Book book)
{
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("售价:%f\n", book.price);
	printf("出版日期:%d-%d-%d\n", book.date.year, book.date.month, book.date.day);
	printf("出版社:%s\n", book.publisher);

}

int main(void)
{
	struct Book b1, b2;
	//b1 = (struct Book)malloc(sizeof(struct Book));
	//b2 = (struct Book)malloc(sizeof(struct Book));
	printf("打印未初始化的书的信息...\n");
	printBook(b1);
	putchar('\n');

	printf("请录入书的信息...\n");
	getInput(b1);	//先传入一个空的结构体,然后再getInput函数内对b1赋值再返回给覆盖b1

	printf("\n######录入完毕现在开始打印验证######\n");
	printf("打印书的信息...\n");
	printBook(b1);
	putchar('\n');

	getchar();
	return 0;
}

结果:

打印未初始化的书的信息...
书名:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?
作者:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?
售价:-107374176.000000
出版日期:-858993460--858993460--858993460
出版社:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?

请录入书的信息...
请输入书名:《书》
请输入作者:zhou
请输入售价:96.3
请输入日期(2015-1-24):2015-2-6
请输入出版社:清华大学

######录入完毕现在开始打印验证######
打印书的信息...
书名:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?
作者:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?
售价:-107374176.000000
出版日期:-858993460--858993460--858993460
出版社:烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?

结论:

/*结构体变量作为函数的参数,修改之后的成员值不能返回到主调函数*/
/*结构体数组作为函数的参数,修改后的元素的成员值能返回到主调函数*/        //与指针相似,
/*结构体指针变量作为函数的参数,修改后的结构体成员的值能返回到主调函数*/        //验证看下个例子

未初始化的指针内部是乱码。
结构体数组和结构体指针都是作为"传址"
而结构体变量则是作为"传值"

结构体指针作为函数的参数:传址

注意:结构体指针作为参数(也就是结构体指针变量)时,一定要先初始化再进行修改不然会出现莫名奇妙的错误。指针指向的值未初始化会是乱码。

例:

//图书管理系统
#include<stdio.h>
#include<stdlib.h>
//void getInput(struct Book *book);//getInput函数声明
//void printBook(struct Book *book);//printBook函数声明

struct Date	//出版时间结构体
{
	int year;//年
	int month;//月
	int day;//日
};

struct Book	//book结构体
{
	char title[128];//书名
	char author[40];//作者
	int price;//售价
	struct Date *date;//出售时间
	char publisher[40];//出版社
};

void getInput(struct Book *book)	//返回结构体的函数参数也是结构体<是一个函数>
{
	printf("请输入书名:");
	scanf("%s", book->title);
	getchar();
	printf("请输入作者:");
	scanf("%s", book->author);
	getchar();
	printf("请输入售价:");
	scanf("%d", &(book->price));
	getchar();
	printf("请输入出版社:");
	scanf("%s", book->publisher);
	getchar();
	book->date = (struct Date*)malloc(sizeof(struct Date));
        printf("请输入日期(2015-1-24):");
        scanf("%d-%d-%d", &(book->date->year), &(book->date->month), &(book->date->day));
        getchar();
	//return 0;//赋值完毕,传出结构体。
}

void printBook(struct Book *book)
{
	printf("书名:%s\n", book->title);
	printf("作者:%s\n", book->author);
	printf("售价:%d\n", book->price);
	printf("出版日期:%d-%d-%d\n", book->date->year, book->date->month, book->date->day);
	printf("出版社:%s\n", book->publisher);

}

int main(void)
{
	struct Book *b1, *b2;

	b1 = (struct Book*)malloc(sizeof(struct Book));    //结构体指针使用前必先初始化。指针的必须性。
	b2 = (struct Book*)malloc(sizeof(struct Book));
	printf("请录入第一本书的信息...\n");
	getInput(b1);	//先传入一个空的结构体,然后再getInput函数内对b1赋值再返回给覆盖b1

	printf("请录入二本书的信息...\n");
	getInput(b2);    //传入结构体指针进行赋值操作。

	printf("\n######录入完毕现在开始打印验证######\n");
	printf("打印第一本书的信息...\n");
	printBook(b1);
	putchar('\n');
	printf("打印第二本书的信息...\n");
	printBook(b2);
	getchar();
	return 0;
}

二.typedef

typedef————对类型重命名、简化复杂类型的别名(类型封装)

例:

#include <stdio.h>

typedef int integer;	//对int重命名为integer

int main(void)
{
	integer a;
	int b;
	
	a = 520;
	b = a;
	
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	printf("size of a = %d\n", sizeof(a));
	
	getchar();
	return 0;
}

结果:

a = 520
b = 520
size of a = 4

typedef与define区别:

define只是机械化的替换(可能会引发类型改变),而typedef是对变量类型的重新命名,类型的封装(不会引发类型改变)。

例:

//define例子
#define PTRINT int*
PTRINT a, b;    ==    int *a, b;//a是int指针变量,而b只是int

//typedef例子
typedef int *PTRINT;    //指向整型类型的指针
PTRINT a, b;    ==    int *a, *b;//a, b都是int指针变量

typedef取多个别名:

typedef int INTEGER, *PTRINT;

typedef与结构体:

#include <stdio.h>
#include <stdlib.h>

typedef struct Date
{
	int year;
	int month;
	int day;
} DATE, *PDATE;//在给结构体取别名时还可以定义一个指针。

int main(void)
{
	DATE *date;
	
	date = (PDATE)malloc(sizeof(DATE));//要用到指针时用PDATE,用到结构体用DATE
	if (date == NULL)
		{
			printf("内存分配失败!回车退出!");
			getchar();
			exit(1);
		}
		
		date->year = 2018;
		date->month = 5;
		date->day = 15;
		
		printf("%d-%d-%d", date->year, date->month, date->day);
	
	getchar();
	return 0;
}

注意:当使用typedef重命名结构体时,大括号的后面不在是结构体变量,而是结构体类型的重命名。

三.联合体(共用体)

共用体所有成员共享同一个内存地址。对成员赋值会覆盖以往的值。只能使用其中一个成员。共用体所占用的内存是能够存储成员中最大成员的尺寸,并不等最大成员的内存。

声明:

union 共用体名称
{
	...
	共用体成员;
	...
};

定义共用体变量和结构体类似。

初始化共用体:一次只初始化一个共用体。

union Date	//声明共用体
{
	int i;
	char ch;
	float f;
} A;//定义共用体A变量

union Data B;	//定义共用体B

union Date A = {520};//初始化共用体的一个成员
union Date B = { .ch = 'c'};//初始化共用体的指定成员

四.枚举类型

枚举:把值一一列举出来。

如果一个变量只有几种可能的值,那么可以将其定义为枚举类型。

声明枚举类型:

enum 枚举类型名称 {枚举常量值1,枚举常量值2,枚举常量值3};

定义枚举变量:

enum 枚举类型名称 枚举变量1,枚举变量2;

声明和定义和结构体差不多,都属于一种集合。

#include <stdio.h>
#include <time.h>    //关于时间的头文件

int main(void)
{
    enum Week {sun, mon, tue, wed, thu, fri, sat} num;//声明Week枚举类型,并定义了一个num变量
    /*
    枚举常量是int类型的,默认第一个常量值从0开始,依次加一。
    可以为第一个常量赋初值,后面的会依次加一。
    例:
    enum Color {red = 10, green, blue};
    enum Color rgb;
    for (rgb = red; rgb <= blue; rgb++)    //rgb本来应该是i
    {
        printf("rgb is %d\n", rgb);
    }
    枚举常量的值在编译就关联好了,替代的宏定义。无法更改枚举常量值。
    如果中间初始化常量,前面的值按默认值初始化,后面的值根据初始化的值依次加一。
    */
    enum Week today;    //定义枚举变量today
    struct tm *p;
    time_t t;

    time(&t);
    p = localtime(&t);
    today = (enum Week)(p->tm_wday);//把后面的整型强制类型转换为enum型
   
     switch (today)
    {
    case mon:
    case tue:
    case wed:
    case thu:
    case fri:
        printf("干活!T_T\n");
        break;
    case sat:
    case sun:
        printf("放假!T——T\n");
        break;
    default:
        printf("Error!\n");
    }

    getchar();
    return 0;
}
枚举的详细:http://www.cnblogs.com/JCSU/articles/1299051.html