Day 15 结构体,共用体, 枚举,链表 。

本文深入探讨了C语言中的结构体概念,包括其定义、内存对齐规则、嵌套使用、枚举类型、共用体及链表操作。通过实例展示了结构体在实际编程中的应用,如整体赋值、成员访问、数组排序及链表操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.结构体:结构体是用户自己定义的一种数据结构。(C语言中是允许用户自己定义数据类型的。)它是用户将不同的基本数据类型组合在一起,构成的一种新的数据类型。
这些组合在一起的数据类型是相互联系的。这种结构体的写法,对于有时候的引用会非常方便。

struct Student
{
	int no;  //int四个字节大小。
	char name[100]; //char 为一个字节大小,此处数组大小为100 所以共占100个字节。
	float score; //float, 大小为四个字节。
	
};  //记得要加封号
此出结构体 struct Student 在定义后共占108个字节

struct Student
{
	int no;  //int四个字节大小。
	char name[100]; //char 为一个字节大小,此处数组大小为100 所以共占100个字节。
	short score; //float, 大小为个2字节。
	
};  //记得要加封号
按理此时应该在定义后占106个字节但是实际。
此出结构体 struct Student 在定义后共占108个字节
原因是结构体在内存中的存储形式决定的。gcc 编译器下,在结构体中默认为最大可以为四个字节,当大于四个字节的类型时按四个字节存储整除对齐,
当数据类型都小于四个字节的时侯,按照可以被类型中最大的那个类型字节数整除。 比如上面的106字节,(上面最大的数据类型为int 四字节)因为不能被四整除,所以扩展到108字节。


2.结构体是允许嵌套的。
struct Date
{
	int year;
	int month;
	int day;
};

struct Student
{
	int No;
	char Name[100];
	float score;
	struct Date Birthday;
};

//此处按理说是118字节,但实际上在定义后为120字节,牢记gcc 编译环境下要为4整除。


struct Bar
{
	char c;

};
struct Demo
{
	char c[2];
	struct Bar b;
};
//此处实际结果为3


struct Bar
{
	char c;
	short b;
};
struct Demo
{
	char c[2];
	struct Bar b;
};
//此处实际结果为6 (要可以整除以2)



struct Bar
{
	char c;
	
};
struct Demo
{
	double b;
	struct Bar b;
};

按理为1+8 为九个字节, 但是要对齐字节, 此处虽然最大的为 double 8字节 但实际结果不整除8,而是要整除以4
//此处实际结果为12 (要可以整除以4)


struct Bar
{
	char c;
	short b;
};
struct Demo
{
	char c[2];
	double b;
	struct Bar b;
};

按理说为 1+2+2+8 =13字节。但是对齐问题要能被四整除。
//此处实际结果为16(要可以整除以4)


struct Bar
{
	char c;
	short b;
};
struct Demo
{
	char c[2];
	short b;
	struct Bar b;
};
1+2+2+2 
//此处结果为 8(要为2整除)

3.有一种办法可以规定按几个字节对齐。
#pragma pack(1) //此处定义按一个字节对齐。

#pragma pack(1)  //规定对齐方式。
struct Bar
{
	char c;
	short b;
};
struct Demo
{
	char c[2];
	short b;
	struct Bar b;
};
1+2+2+2 
//此处结果为 7(要为1整除)


小结: 关于在编译器下是否规定按几字节对齐的问题,
 如果规定按一个字节对齐,虽然省下了空间但会降低cpu的处理数据的效率,
 如果不规定按照编译器默认的来则会提高处理效率但会浪费空间资源。
 另外在一些实际项目中往往会规定按几字节处理对齐,这会方便通信交流。
 
 4.结构体不允许使用关系运算符。
 
 
 5.结构体允许整体赋值。
 struct Date
{
	int year;
	int month;
	int day;
};

struct Student
{
	int No;
	char Name[100];
	float score;
	struct Date Birthday;
};

 struct Student s  = {1, "zhangsan", 99.5, {2019, 10,22}};
 
 
 6.共用体 : 要注意不能把共用体作为函数参数。
 //共用体与结构体的声明, 定义非常类似, 不同的是存储的不同
 union Demo
{
	int i;
	short s;
	char c;
};

union IP
{
	unsigned char val[4];
	unsigned int data;
};

union AAA
{
	int i;
	char c;
};
//共用体不能作为函数的参数
void fn(union AAA a)  //此种写法为错误的。
{

}

int main(void)
{
/*	union AAA a;
	a.i = 0x00000001;

	if(a.c == 1)
	{
		puts("small");
	}
	else
	{
		puts("big");
	}*/

	union Demo d;
	d.i = 12345678;
	d.s = 12345;
	d.c = 100;

	union Demo *p;
	p = &d;

	p->c = 100;


	printf("%d\n", sizeof(union Demo));

	return 0;
}






***
***
用共用体的方法判断电脑是大端还是小端存储模式。

union Demo 
{

	char c;
	int i;   //共开了一个四字节大小的空间。
	

};
int main(void)
{

	union Demo d;
	d.i = 1;  //给i些如数字1

	printf("%d\n", d.c);  //因为共用空间所以此处调用一个字节的char 型变量也是可以打印出值的。打印出数字1说明为小端模式(0x00000001)
	return 0;
}




7.枚举
(1) 不应该把整型给枚举类型赋值,虽然这样做编译器不会报错,但这要程序会乱掉。
enum WEEK  //枚举是可以确定一个范围的。
{
	Sun = 3, Mon, Tue, Wen, Thu, Fri, Sat
};

int main(void)
{
	enum WEEK w;
	w = Fri;
	printf("%d\n" ,w);  //此处w为8   如果前面没有 Sun = 3; 则此处的w为5

	int k;
	w = k;       //
	printf("%d\n", k);
/*	w = Mon;   //可以取的值都为enum 内规定的值。

	switch(w)
	{
	case Mon:
		break;
	case Sun:
		break;
	}*/
	
	return 0;
}






***
***
使用结构体,初始化并打印出结构体里面的元素值。使用 scanf() 向结构体中输入相应的值并且打印出来。


struct Date
{

	int year;
	int month;
	int day;

};

struct Student
{

	int no;
	char name[100];
	float score;
	struct Date brithday;  //结构体嵌套。

};
//打印结构体中成员的值。
void printStudent(struct Student *st)
{
	printf("%d, %s, %f  %4d-%2d-%2d\n", st ->no, st ->name, st ->score, st ->brithday.year, st ->brithday.month, st ->brithday.day);

}

//输入相应的值
void scanStudent(struct Student *st)
{

	 scanf("%d%s%f%d%d%d", &st ->no, st ->name, &st ->score, &st ->brithday.year, &st -> brithday.month, &st ->brithday.day);
	
}


//打印结构体数组。
void printStudents(struct Student *st, int len)
{

	int i;
	for(i = 0; i < len; ++i)
	{
	
		printStudent(st + i); //调用打印一条结构体的函数。
	}
}
void sortStudent(struct Student *st, int len)  //排序可以按照结构体中的任何一个成员来比较排序。
{

	int i,j;
	for(i = 0; i < len - 1; ++i)
	{
		for(j = i + 1; j < len; ++j)
		{
		
		//	if(st[i].score  > st[j].score) //比较所得分数时访问成员,为float 类型直接比较即可。
			if(strcmp(st[i].name, st[j].name))  //如果按照姓名排序,则需要调用strcmp函数对两个字符串进行比较。
			{
				struct Student t = st[i];  //此处的数据类型要匹配,为结构体类型 struct Student
				st[i] = st[j];             //此处可以这样是因为,结构体可以整体赋值。
				st[j] = t;
			}

		}
	
	}

}

int scorCmp(const void *p1, const void *p2)  //此函数要自己定义。
{
	struct Student * q1 = (struct Student *)p1; //此处要指针类型强转  
	struct Student * q2 = (struct Student *)p2;

/*	if(q1 ->score > q2 ->score)
	{
	
		return 1;
	}
	else if(q1 ->score < q2 ->score)
	{
		return -1;
	}
	else
	{
		return 0;
	}*/

	if(strcmp(q1 -> name, q2 ->name) > 0)  //在此处只需要判断比较成员的大小,返回结果即可自动排序。
	{
	
		return 1;
	}
	else if(strcmp(q1 ->name, q2 ->name) < 0)
	{
		return -1;
	}
	else
	{
		return 0;
	}

}




int main(void)
{

	struct Student s[3] = {{1, "lisa", 99.8, {1990, 6, 8}},
						 {2, "kate", 99.7,{1991, 6, 8}},
						 {3, "summer", 99.6, {1992, 6, 8}}};
	
	qsort(s, sizeof(s) /sizeof(s[0]), sizeof(s[0]), scorCmp);  //此句为调用系统的qsort函数。
 //scorCmp为一个函数指针变量。     
//	sortStudent(s,sizeof(s) / sizeof(s[0]));  //此句为自己写的排序

	printStudents(s, sizeof(s) / sizeof(s[0]));

	return 0;

}




***
***
链表:

#include <stdlib.h>
struct Node
{

	int data;
	struct Node *next;
};
//前加
void push_front(struct Node *pHead, int n)
{

	struct Node *pNew = malloc(sizeof(struct Node));
	pNew ->data = n;
	pNew ->next = pHead ->next;
	pHead ->next = pNew;
}

//判断是否为空
int isEmpty(struct Node *pHead)
{

	return pHead ->next == 0;
}

//后加
void push_back(struct Node *pHead, int n)
{

	struct Node *pNew = malloc(sizeof(struct Node));
	pNew ->data = n;
	pNew ->next = NULL;


	struct Node *p = pHead ->next;
	if(isEmpty(pHead))
	{
	
		return;

	}
	while(p ->next != NULL)
	{
		p = p ->next;
	}
	p ->next = pNew;

}

//前删
void pop_front(struct Node *pHead)
{

	if(isEmpty(pHead))
	{
		return ;
	}
	struct Node *p = pHead ->next;
	pHead -> next = p -> next;
	free(p);

}

//后删
void pop_back(struct Node *pHead)
{

	if(length(pHead) == 0)
	{
		return ;
	}
	else if(length(pHead) == 1)
	{
		pop_front(pHead);
	}
	else
	{
		struct Node *p = pHead->next;
		while(p->next->next != NULL)
		{
			p = p->next;
		}
		free(p->next);
		p->next = NULL;
	}

}


void showList(struct Node *pHead)
{
	struct Node *p = pHead ->next;
	while(p !=NULL)
	{
	
		printf("%d,", p ->data);
		p = p ->next;

	}
	puts("\b ");

}
//找节点
size_t findNode(struct Node *phead)
{
	struct Node *p = phead ->next;
	size_t couter = 0;
	while(p != NULL)
	{
	
		++couter;
		p = p ->next;
	}
	return couter;

}
int main(void)
{

	struct Node head = {-1, NULL};
	struct Node *phead = &head;

/*	push_front(&head, 4);
	push_front(&head, 3);
	push_front(&head, 2);
	push_front(&head, 5);*/
	push_front(&head, 4);
	push_back(phead, 3);
	push_back(phead, 2);
	push_back(phead, 5);

	pop_front(phead);
	showList(&head);
	printf("%d\n", findNode(&head));
	pop_front(phead);
	return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值