【C语言基础】结构体与共同体

1 结构体的定义和初始化

将不同数据类型的数据组织成一个组合项的指定的数据结构,称为结构体。

1.1 结构体声明

结构体的关键字为struct,以下为声明格式:

struct 结构体名

{

    成员列表

};

例:

struct Student
{
	char* cName;
	int cAge;
	int cID;
	int cGrade;
};

1.2 结构体定义

结构体定义有两种形式:

  • 先声明结构体,再进行定义结构体变量;
  • 在声明结构体时,同时定义结构体变量;

例1-先声明再定义

struct Student
{
	char* cName;
	int cAge;
	int cID;
	int cGrade;
};

main() {
	struct Student student1;
	struct Student student2;
}

例2-声明的同时进行定义

struct Student
{
	char* cName;
	int cAge;
	int cID;
	int cGrade;
	struct date birthday;
} student1, student2;

结构体中的成员也可以是另外一个结构体,也就是说,结构体可以嵌套。上例2中,struct Student结构体中有一个成员是struct date结构体。

1.3 结构体变量的初始化和引用

结构体变量的初始化就是给各个成员变量赋初值,可以在定义的同时进行初始化赋值,此时是整体赋值;也可以定义好之后再赋值,但不可以进行整体赋值,只能各个成员变量分别赋初值。

结构体变量中的成员可以进行赋值、存取或运算,引用结构体变量中的成员的格式为:

结构体变量名.成员名

例:

#include <stdio.h>
struct date
{
	int year;
	int month;
	int day;
};

struct Student
{
	char* cName;
	int cAge;
	int cGrade;
	struct date cBirthday;	//嵌套结构体struct date
} student1, student2 = { "Lily", 10, 3, {2014,1,1} };
//student1定义时未做初始化,student2在定义的同时进行了初始化

main() {
	//student1已经定义,不能再进行整体初始化,只能分别为各个成员变量进行赋值初始化
	student1.cName = "Sam";
	student1.cAge = 12;
	student1.cGrade = 5;
	student1.cBirthday.year = 2012;
	student1.cBirthday.month = 11;
	student1.cBirthday.day = 6;
	printf("student1 information =====>\n");
	printf("name: %s\n", student1.cName);
	printf("age: %d\n", student1.cAge);
	printf("grade: %d\n", student1.cGrade);
	printf("birthday: %d.%02d.%02d\n", student1.cBirthday.year, student1.cBirthday.month, student1.cBirthday.day);
	printf("\n");

	printf("student2 information =====>\n");
	printf("name: %s\n", student2.cName);
	printf("age: %d\n", student2.cAge);
	printf("grade: %d\n", student2.cGrade);
	printf("birthday: %d.%02d.%02d\n", student2.cBirthday.year, student2.cBirthday.month, student2.cBirthday.day);
	//修改student2的部分成员值;
	student2.cBirthday.month = 5;
	student2.cBirthday.day = 21;
	printf("correct birthday...\n");
	printf("birthday: %d.%02d.%02d\n", student2.cBirthday.year, student2.cBirthday.month, student2.cBirthday.day);
	printf("\n");

	struct Student student3 = { "Tom", 11, 4, {2013, 6, 30} };
	printf("student3 information =====>\n");
	printf("name: %s\n", student3.cName);
	printf("age: %d\n", student3.cAge);
	printf("grade: %d\n", student3.cGrade);
	printf("birthday: %d.%02d.%02d\n", student3.cBirthday.year, student3.cBirthday.month, student3.cBirthday.day);
	printf("correct grade...\n");
	student3.cGrade++;
	printf("grade: %d\n", student3.cGrade);
}

运行结果

2 结构体数组

元素类型是结构体的数组就是结构体数组。同其它元素类型的结构体变量一样,结构体数组变量可以在定义的时候直接进行赋初值,也可以在定义后再进行赋值

例:

#include <stdio.h>
struct Product
{
	char* name;
	char* color;
	int price;
} product1[2], product2 = {"bag", "white", 30};

main()
{
	int i;
	product1[0].name = "table";
	product1[0].color = "green";
	product1[0].price = 100;
	product1[1].name = "chair";
	product1[1].color = "green";
	product1[1].price = 80;
	for (i = 0; i < 2; i++)
	{
		printf("product1 element %d: =====>\n", i + 1);
		printf("name: %s\n", product1[i].name);
		printf("color: %s\n", product1[i].color);
		printf("price: %d\n", product1[i].price);
	}
	printf("------------------------------------------------------\n");
	printf("\n");

	printf("product2: =====>\n");
	printf("name: %s\n", product2.name);
	printf("color: %s\n", product2.color);
	printf("price: %d\n", product2.price);
	printf("------------------------------------------------------\n");
	printf("\n");

	struct Product product3[5] = {
		{"bag", "pink", 50},
		{"table", "black", 80},
		{"chair", "white", 100},
		{"sofa", "blue", 1000},
		{"lamp", "red", 130},
	};
	for (i = 0; i < 5; i++)
	{
		printf("product3 element %d: =====>\n", i + 1);
		printf("name: %s\n", product3[i].name);
		printf("color: %s\n", product3[i].color);
		printf("price: %d\n", product3[i].price);
		printf("\n");
	}
}

运行结果

3 结构体指针

3.1 结构体指针变量的定义与使用

3.1.1 结构体指针变量定义

struct 结构体名 *结构体指针变量名

3.1.2 结构体指针变量的引用

struct Student *pStudent;

(*pStudent).成员名;

struct Student *pStudent;

pStudent->成员名;

例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct iProduct
{
	char* name;
	char* color;
	int price;
} product;

main()
{
	struct iProduct* pProduct;
	pProduct = &product;

	// 为 name 和 color 分配内存
	pProduct->name = (char*)malloc(20 * sizeof(char)); // 假设最大长度为 20
	pProduct->color = (char*)malloc(20 * sizeof(char));

	// 对各成员进行赋值
	strcpy(pProduct->name, "table");
	strcpy(pProduct->color, "black");
	(*pProduct).price = 200;

	printf("----- The Product information -----\n");
	printf("name: %s\n", product.name);
	printf("color: %s\n", product.color);
	printf("price: %d\n", product.price);

	// 释放分配的内存
	free(pProduct->name);
	free(pProduct->color);
	}
}

运行结果

3.2 结构体数组指针

结构体指针变量可以指向结构体数组,此时指针变量的值就是结构体数组的首地址;结构体指针变量也可以指向结构体数组中的某一元素,此时指针变量的值就是该元素的首地址。

struct Product *p1, *p2;

p1 = product;              //结构体指针指向结构体数组product

p2 = &product[3];        //结构体指针指向结构体数组product的第3个元素product[3]

例:

#include <stdio.h>
struct iProduct
{
	char* name;
	char* color;
	int price;
} product;

main()
{
	// 结构体数组指针
	struct iProduct product2[5] = {
		{"bag", "pink", 50},
		{"table", "black", 80},
		{"chair", "white", 100},
		{"sofa", "blue", 1000},
		{"lamp", "red", 130},
	};
	struct iProduct* p1;
	p1 = product2;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("----- The Product2-%d information -----\n", i+1);
		printf("name: %s\n", p1->name);
		printf("color: %s\n", p1->color);
		printf("price: %d\n", p1++->price);	//此处,先取p1当前指向元素的成员值进行使用,再指向下一个元素的地址。
	}

	struct iProduct* p2;
	p2 = &product2[3];
	printf("----- The product2[3] information -----\n");
	printf("name: %s\n", p2->name);
	printf("color: %s\n", p2->color);
	printf("price: %d\n", p2->price);
}

运行结果:

3.3 结构体作函数的参数

结构体作为函数的参数有三种:

结构体作为函数的参数
参数形式传递内容说                 明
结构体变量值传递

实参与形参均为同类型的结构体变量。空间与时间上的消耗大。

如:void Display(struct Student student);

结构体变量的指针地址传递如:void Display(struct Student *student);
结构体变量的成员值传递

函数传递参数与普通变量作为实参相同。

如:Socre(student1.score[3]);

例-结构体变量的指针作为函数参数

#include <stdio.h>
struct iGame
{
	char* name;
	float result[5];
} game = { "xiaohong", 9.5, 9.6, 9.5, 9.8, 9.0 };

void game_result(struct iGame* gm)
{
	int i;
	float iMax = gm->result[0];
	float iMin = gm->result[0];
	float iSum = 0;
	printf("----- %s -----\n", gm->name);
	printf("result: ");
	for (i = 0; i < 5; i++)
	{
		if (gm->result[i] > iMax)
			iMax = gm->result[i];
		if (gm->result[i] < iMin)
			iMin = gm->result[i];
		iSum += gm->result[i];
		printf("%.2f    ", gm->result[i]);
	}
	printf("\n");
	iSum = iSum - iMax - iMin;
	float score = (iSum / 3);
	printf("Final Score: %.2f\n", score);
}

main()
{
	struct iGame* p1;
	p1 = &game;
	game_result(p1);

	struct iGame game_list[4] = {
		{ "lily", 10, 9.8, 9.9, 9.7, 10 },
		{ "lee", 8.9, 9.1, 9.0, 8.8, 8.5 },
		{ "sam", 9.3, 9.2, 9.2, 9.2, 9.4 },
		{ "tony", 6.0, 6.5, 5.7, 5.9, 5.9 },
	};
	struct iGame* p2;
	p2 = game_list;
	int i;
	for (i = 0; i < 4; i++, p2++)
		game_result(p2);
}

运行结果

4 链表

链表是一种常见的数据结构,是线性链式存储结构。链表中的每个元素(即结点)分为两部分,一部分是数据部分,另一部分是指针部分。数据部分用来存放该元素所包含的数据,指针部分指向下一个元素地址。最后一个元素的指针部分指向NULL,即空地址。每个链表都有一个头指针变量,指向链表第一个元素的地址。

链表必须用指针来实现,因此链表中的结点应该包含一个指针变量,用来存放下一个结点的地址。

链表的结点是动态创建的,需要在创建前为其分配内存。

内存空间动态分配和释放函数
函数函数原型
mallocvoid *malloc(unsigned int size);

在内存中动态分配一块size大小的空间。

返回:指针

例:(int *)malloc(sizeof(int));

callocvoid *calloc(unsigned n, unsigned size);

在内存中动态分配n个长度为size的连续内存空间数组。

返回:指针

freevoid free(void *ptr);

释放指针ptr没有用到的空间。ptr是最近一次调用calloc或malloc函数时的返回值。

返回:无

例1-创建链表及链表结点的插入与删除

#include <stdio.h>
#include <stdlib.h>
struct cStudent
{
	char name[20];
	int number;
	struct cStudent* pNext;
};

int cCount;

struct cStudent* Creat()		//创建链表
{
	struct cStudent* pHead = NULL;	//初始链表头指针定义为空
	struct cStudent* pEnd, * pNew;
	cCount = 0;		//初始化链表长度为0
	pEnd = pNew = (struct cStudent*)malloc(sizeof(struct cStudent));	//为结点分配内存空间
	printf("Input Name and Number (0 to stop):\n");
	scanf("%s", &pNew->name);		//写值到pNew指向空间的name变量中
	scanf("%d", &pNew->number);		//写值到pNew指向空间的number变量中

	while (pNew->number != 0)
	{
		cCount++;			//链表长度+1
		if (cCount == 1)
		{
			pNew->pNext = pHead;	//pNew为链表第一个结点,指向空间中pNext指针变量指向为空
			pEnd = pNew;		//pEnd指向新加入的结点pNew所指向的地址
			pHead = pNew;		//pNew为链表的第一个结点,即首结点,pHead指向首结点pNew所指向的地址
		}
		else
		{
			pNew->pNext = NULL;		//定义新结点pNew指向空间中pNext指针变量指向空
			pEnd->pNext = pNew;		//将pEnd当前指向空间,即上一结点的地址,中的pNext指针变量指向新结点pNew
			pEnd = pNew;			//改变pEnd指向空间到新结点pNew的地址
		}

		pNew = (struct cStudent*)malloc(sizeof(struct cStudent));	//再次为新结点分配内存空间
		scanf("%s", &pNew->name);
		scanf("%d", &pNew->number);
	}
	free(pNew);		//释放没有用到的空间
	return pHead;
}

struct cStudent* Insert(struct cStudent* pHead)    //在链表开头插入新结点
{
	struct cStudent* pNew;
	printf("----- Insert a member to header -----\n");

	pNew = (struct cStudent*)malloc(sizeof(struct cStudent));	//为新结点分配内存空间
	scanf("%s", &pNew->name);
	scanf("%d", &pNew->number);

	pNew->pNext = pHead;	//新结点pNew指向空间中的pNext指针指向pHead指向的地址
	pHead = pNew;		//pHead指向新结点pNew的指向的地址
	cCount++;
	return pHead;
}

void Delete(struct cStudent* pHead, int delete_index)    //删除链表指定位置的结点
{
	struct cStudent* pTemp;
	struct cStudent* pPre;
	pTemp = pHead;	//pTemp指向头指针pHead指向的地址
	pPre = pTemp;	//pPre指向pTemp指向的地址

	printf("----- Delete member %d -----\n", delete_index);
	for (int i = 0; i < delete_index; i++)
	{
		pPre = pTemp;			//pPre指向pTemp指向的地址,即指向链表当前成员
		pTemp = pTemp->pNext;	//pTemp指向它的指向空间中的pNext指针指向的地址,即指向链表下一个成员
	}
	pPre->pNext = pTemp->pNext;	//pPre指向空间中的pNext指向pTemp下一个成员的地址,即指向下下个链表成员
	free(pTemp);
	cCount--;
}

Print_Student(struct cStudent* pHead)
{
	struct cStudent* pTemp;
	int i = 1;

	printf("----- %d member in this table -----\n", cCount);
	printf("\n");
	pTemp = pHead;

	while (pTemp != NULL)
	{
		printf("Member %d -->\n", i);
		printf("Name: %s\n", pTemp->name);
		printf("Number: %d\n", pTemp->number);
		pTemp = pTemp->pNext;
		i++;
	}
}

main()
{
	struct cStudent* pHead;
	pHead = Creat();
	Print_Student(pHead);

	pHead = Insert(pHead);
	Print_Student(pHead);

	Delete(pHead, 3);
	Print_Student(pHead);
}

运行结果

Input Name and Number (0 to stop):
A 10
B 20
C 30
D 40
E 50
end 0
----- 5 member in this table -----

Member 1 -->
Name: A
Number: 10
Member 2 -->
Name: B
Number: 20
Member 3 -->
Name: C
Number: 30
Member 4 -->
Name: D
Number: 40
Member 5 -->
Name: E
Number: 50
----- Insert a member to header -----
Lily 100
----- 6 member in this table -----

Member 1 -->
Name: Lily
Number: 100
Member 2 -->
Name: A
Number: 10
Member 3 -->
Name: B
Number: 20
Member 4 -->
Name: C
Number: 30
Member 5 -->
Name: D
Number: 40
Member 6 -->
Name: E
Number: 50
----- Delete member 3 -----
----- 5 member in this table -----

Member 1 -->
Name: Lily
Number: 100
Member 2 -->
Name: A
Number: 10
Member 3 -->
Name: B
Number: 20
Member 4 -->
Name: D
Number: 40
Member 5 -->
Name: E
Number: 50

5 共用体

共用体看着像结构体,但与结构体是不同的。结构体定义了一个由多个数据成员组成的特殊类型,共用体定义了一块为所有数据成员所共享的内存。

共用的关键字是union。

共用体在同一时刻只有一个值 ,属于某一个数据成员,共用体的大小是最大数据成员的大小。

共用体的定义形式与引用与结构体类似,但是因为共用一块内存,当改变共用体的一个成员,别的成员也会随之改变。

共用体结构类型的数据特点:

  • 共用体中只有一个成员起作用,其它的成员不起作用。起作用的成员是最后一次存放的成员,当存入一个新成员后,原有的成员就失去作用。
  • 共用体所占内存空间等于最大成员变量所占的内存空间。
  • 共用体变量的地址和其各个成员的地址都是同一个地址。
  • 不能对共用体变量名赋值,也不可引用共用体变量名来得到一个值。
  • 不能用共用体变量作为函数的参数或返回值。当需要在函数间你传递共用体时,可以使用指向共用体变量的指针来实现。

例如,以下赋值语句:

variable.index = 5;

variable.Char = 'A';

variable.Float = 4.4;

当进行完这一系列赋值后,只有variable.Float是有效的,这时用printf("%d", variable.index);语句输出是错误的。

例:

#include <stdio.h>
union {
	int num;
	char ch[2];
} uWord;

main()
{
	uWord.num = 0x1234;
	printf("Current hex number: 0x%x\n", uWord.num);
	printf("Current char[0]: %c - 0x%x\n", uWord.ch[0], uWord.ch[0]);
	printf("Current char[1]: %c - 0x%x\n", uWord.ch[1], uWord.ch[1]);
	uWord.ch[1] = 'a';
	printf("After change,\n");
	printf("Current hex number: 0x%x\n", uWord.num);
	printf("Current char[0]: %c - 0x%x\n", uWord.ch[0], uWord.ch[0]);
	printf("Current char[1]: %c - 0x%x\n", uWord.ch[1], uWord.ch[1]);
}

运行结果

6 枚举类型的使用

枚举类型是将变量的值一一列举出来,变量的值只限于这些列举出来的范围。

定义形式:

enum 枚举名

{

    标识符[=整型常数],

    标识符[=整型常数],

    ...,

    标识符[=整型常数],

} 枚举变量;

定义时,如果没有对标识符进行初始化,"=整型常数"部分可以省略。第一个标识符如果没有进行初始化,它的值为0,其它标识符如果没有进行初始化,其值为前一个标识符的值+1。

如:

enum week
{
	Sunday,
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday = 50,
	Saturday,
} workday;

main()
{
	enum week weekend;
	weekend = Sunday;		//正确。weekend的值是0,即Sunday的值
	workday = Monday;		//正确。workday的值是1,即Monday的值
	workday = Friday;		//正确。workday的值是50,即Friday的值
	weekend = Saturday;		//正确。weekend的值是51,即Saturday的值
	Monday = 90;			//错误。枚举元素是常量,不能进行赋值
	workday = 5;			//错误。类型不同,需进行强制类型转换
	workday = (enum week)5;	 //正确
}
  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值