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,即空地址。每个链表都有一个头指针变量,指向链表第一个元素的地址。
链表必须用指针来实现,因此链表中的结点应该包含一个指针变量,用来存放下一个结点的地址。
链表的结点是动态创建的,需要在创建前为其分配内存。
函数 | 函数原型 | |
---|---|---|
malloc | void *malloc(unsigned int size); | 在内存中动态分配一块size大小的空间。 返回:指针 例:(int *)malloc(sizeof(int)); |
calloc | void *calloc(unsigned n, unsigned size); | 在内存中动态分配n个长度为size的连续内存空间数组。 返回:指针 |
free | void 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; //正确
}