一.结构体:
1.类型定义:
struct 结构体名 {
数据类型1 成员变量1;
数据类型2 成员变量2;
数据类型3 成员变量3;
...
};
struct student {
char name[32];
char sex;
int age;
int score;
};
2.结构体变量定义:
存储类型 数据类型 变量名;
3.结构体元素初始化:
1.全部初始化:
struct student stu = {"zhangsan", 'm', 18, 90};
2.局部初始化:
struct student stu = {
.name = {"zhangsan"}, //没有给定初值的元素都被赋值为0
.score = 90,
};
4.结构体成员访问:
. :结构体变量类型访问成员变量 .
-> :结构体指针类型访问成员变量 ->
结构体访问成员变量最终的类型由成员变量的类型决定。
例:
#include <stdio.h>
struct datetime {
struct date {
int year;
int mon;
int day;
}d;
struct time {
int hour;
int min;
int sec;
}t;
};
struct student {
char name[32];
char sex;
int age;
int score;
};
int main(void)
{
struct datetime dt = {
{2025, 7, 29}, {15, 9, 30},
};
struct datetime dt1 = {
.t = {
.hour = 15,
.min = 10,
},
};
struct student stu = {"zhangsan", 'm', 19, 100};
struct student s = {
.name = "lisi",
.score = 80,
};
struct student *pstu = NULL;
struct datetime *pdt = NULL;
printf("%04d-%02d-%02d\n", dt.d.year, dt.d.mon, dt.d.day); // 输出dt的日期和时间
printf("%02d:%02d:%02d\n", dt.t.hour, dt.t.min, dt.t.sec);
printf("%04d-%02d-%02d\n", dt1.d.year, dt1.d.mon, dt1.d.day); // 输出dt1的日期和时间
printf("%02d:%02d:%02d\n", dt1.t.hour, dt1.t.min, dt1.t.sec);
printf("姓名:%s\n", stu.name); // 输出stu的信息
printf("性别:%c\n", stu.sex);
printf("年龄:%d\n", stu.age);
printf("成绩:%d\n", stu.score);
pstu = &s;
printf("姓名:%s\n", pstu->name); // 通过指针输出s的信息
printf("性别:%c\n", pstu->sex);
printf("年龄:%d\n", pstu->age);
printf("成绩:%d\n", pstu->score);
pdt = &dt;
printf("%04d-%02d-%02d %02d:%02d:%02d\n", pdt->d.year, pdt->d.mon, pdt->d.day, pdt->t.hour, pdt->t.min, pdt->t.sec); // 通过指针输出dt的完整信息
return 0;
}
从终端接收:
#include <stdio.h>
#include <string.h>
struct student
{
char name[32];
char sex;
int age;
int score;
};
int main(void)
{
struct student s;
struct student *pstu = NULL;
char str[32] = {0};
char tmpsex = 0;
int tmpage = 0;
int tmpscore = 0;
pstu = &s;
gets(pstu->name);
scanf(" %c", &pstu->sex);
scanf("%d", &pstu->age);
scanf("%d", &pstu->score);
printf("姓名:%s\n", s.name);
printf("性别:%c\n", s.sex);
printf("年龄:%d\n", s.age);
printf("成绩:%d\n", s.score);
return 0;
}
5.结构体的存储:
内存对齐:
struct student {
char name[32];
char sex;
int age;
int score; //32(name) + 1(sex) + 3(填充) + 4(age) + 4(score) = 44 字节
};
结构体成员必须存放在内存地址为自身类型长度整数倍的内存单元中,
结构体的大小必须为自身最大类型长度的整数倍。
6.结构体传参:
1.传值:
void fun(struct student tmp);
2.传地址:
void fun(struct student *ptmp);
#include <stdio.h>
#include <string.h>
struct student {
char name[32];
char sex;
int age;
int score;
};
struct student GetStuInfo(void)
{
struct student tmp;
gets(tmp.name);
scanf(" %c", &tmp.sex);
scanf("%d", &tmp.age);
scanf("%d", &tmp.score);
return tmp;
}
int PutStuInfo(struct student tmp)
{
printf("姓名:%s\n", tmp.name);
printf("性别:%c\n", tmp.sex);
printf("年龄:%d\n", tmp.age);
printf("成绩:%d\n", tmp.score);
return 0;
}
void GetStuInfoByPoint(struct student *pstu)
{
gets(pstu->name);
scanf(" %c", &pstu->sex);
scanf("%d", &pstu->age);
scanf("%d", &pstu->score);
return;
}
void PutStuInfoByPoint(struct student *pstu)
{
printf("姓名:%s\n", pstu->name);
printf("性别:%c\n", pstu->sex);
printf("年龄:%d\n", pstu->age);
printf("成绩:%d\n", pstu->score);
return;
}
int main(void)
{
struct student ret;
memset(&ret, 0, sizeof(ret)); //初始化,内存置位
GetStuInfoByPoint(&ret);
PutStuInfoByPoint(&ret);
#if 0
ret = GetStuInfo();
PutStuInfo(ret);
#endif
return 0;
}
传地址更好,因为实参将8字节拷贝给形参,避免结构体大空间的拷贝。
7.结构体数组:
格式:数据类型 数组名[元素个数];
8.结构体数组初始化:
struct student s[3] = {
{"zhangsan", 'm', 19, 100},
{"lisi", 'f', 18, 90},
{"wanger", 'm', 19, 60},
};
struct student s[3] = {
[1] = {
.name = "zhangsan",
.score = 90,
},
};
9.结构体数组传参:
int fun(struct student *pstu, int len);
二.共用体:
1.共用体也称为联合体:
共用体每个成员变量的空间共享的,
结构体每个成员变量的空间是独立的,
多用于函数传参使用。
2.数据类型定义:
union 共用体名 {
数据类型1 成员变量1;
数据类型2 成员变量2;
数据类型3 成员变量3;
...
};
3.使用共用体判断内存大小端:
内存低地址存放低数据位,内存高地址存放高数据位,内存小端,
内存低地址存放高数据位,内存高地址存放低数据位,内存大端。
#include <stdio.h>
union s {
char a;
int b;
};
int main(void)
{
union s s1;
s1.b = 1;
if(s1.a)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
#if 0
int Num = 0;
char *p = NULL;
Num = 0x11223344;
p = (char *)&Num;
if (0x11 == *p)
{
printf("大端存储\n");
}
else if (0x44 == *p)
{
printf("小端存储\n");
}
#endif
return 0;
}
三.枚举:
1. 枚举定义一些枚举常量。
2.定义形式:
enum 枚举类型 {
常量1,
常量2,
常量3,
...
};
3.特性:
枚举常量均为int类型,且第一个枚举常量的值默认为0,后续枚举常量的值总是前一个常量的
值+1,
枚举常量可以在定义时被赋值。
例:
#include <stdio.h>
enum weekday {
MONDAY = 1,
TUESDAY,
WEDNESDAY,
THURDAY,
FRIDAY,
SATURSDAY,
SUNDAY,
};
int main(void)
{
enum weekday day;
printf("请输入今天是周几:\n");
scanf("%d", (int *)&day);
switch (day)
{
case MONDAY:printf("尾号1和6限行\n");break;
case TUESDAY:printf("尾号2和7限行\n");break;
case WEDNESDAY:printf("尾号3和8限行\n");break;
case THURDAY:printf("尾号4和9限行\n");break;
case FRIDAY:printf("尾号5和0限行\n");break;
case SATURSDAY:
case SUNDAY:
printf("不限行\n");
}
return 0;
}
四.练习:
1.计算并显示一个给定日期在当年是第几天,以及该年还剩余多少天:
#include <stdio.h>
struct date {
int year;
int mon;
int day;
};
int GetDate(struct date *pday)
{
scanf("%d-%d-%d", &pday->year, &pday->mon, &pday->day);
return 0;
}
int IsLeepYear(int tmpyear)
{
if (((0 == tmpyear % 4) && (tmpyear % 100 != 0)) || (0 == tmpyear % 400))
{
return 1;
}
return 0;
}
int GetDayOfYear(struct date *pday)
{
int i = 0;
int sum = 0;
for (i = 1; i < pday->mon; i++)
{
switch (i)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:sum += 31;break;
case 4:
case 6:
case 9:
case 11:sum += 30;break;
case 2: sum += (IsLeepYear(pday->year) ? 29 : 28);
}
}
sum += pday->day;
return sum;
}
int GetLeftDayOfYear(struct date *pday)
{
int total = 0;
if (IsLeepYear(pday->year))
{
total = 366;
}
else
{
total = 365;
}
return total - GetDayOfYear(pday);
}
int main(void)
{
struct date day;
int cnt = 0;
GetDate(&day);
cnt = GetDayOfYear(&day);
printf("%04d-%02d-%02d为该年的第%d天\n", day.year, day.mon, day.day, cnt);
cnt = GetLeftDayOfYear(&day);
printf("该年剩余%d天\n", cnt);
return 0;
}
2.单词顺序反转,并保持内部的顺序不变(不使用逆序算法):
#include <stdio.h>
#include <string.h>
int strswap(char *phead, char *ptail)
{
char tmp = 0;
while (phead < ptail)
{
tmp = *phead;
*phead = *ptail;
*ptail = tmp;
phead++;
ptail--;
}
return 0;
}
int wordswap(char *pstr)
{
char *pstart = NULL;
char *pend = NULL;
//先对整体交换
strswap(pstr, pstr+strlen(pstr)-1);
pstart = pstr;
while (1)
{
pend = strchr(pstart, ' '); //使用strchr找到下一个空格
if (pend != NULL)
{
strswap(pstart, pend-1); //如果找到了空格,就反转这个单词
}
else
{
strswap(pstart, pstart + strlen(pstart) - 1);
break;
}
pstart = pend + 1;
}
#if 0
pstart = pstr;
pend = pstart;
while (*pend != '\0')
{
pend = pstart;
while (*pend != '\0' && *pend != ' ')
{
pend++;
}
strswap(pstart, pend-1);
pstart = pend + 1; //合法但危险
}
#endif
return 0;
}
int main(void)
{
char str[256] = {0};
gets(str);
wordswap(str);
printf("str = %s\n", str);
return 0;
}
3.学生信息录入 + 输出:
#include <stdio.h>
struct student {
char name[32];
char sex;
int age;
int score;
};
int GetAllStuInfo(struct student *pstu, int len)
{
int i = 0;
int n = 0;
printf("请输入学生个数:\n");
scanf("%d", &n);
getchar(); // 读取掉输入个数后的回车,避免后续 gets 被跳过
for (i = 0; i < n; i++)
{
gets(pstu->name);
scanf(" %c", &pstu->sex);
scanf("%d", &pstu->age);
scanf("%d", &pstu->score);
pstu++;
#if 0
gets((pstu+i)->name);
scanf(" %c", &(pstu+i)->sex);
scanf("%d", &(pstu+i)->age);
scanf("%d", &(pstu+i)->score);
// gets(pstu[i].name); //\n会导致后续输入乱掉
scanf("%s", pstu[i].name);
scanf(" %c", &pstu[i].sex);
scanf("%d", &pstu[i].age);
scanf("%d", &pstu[i].score);
#endif
getchar();
}
return n;
}
int PutAllStuInfo(struct student *pstu, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("姓名:%s\n", pstu[i].name);
printf("性别:%c\n", pstu[i].sex);
printf("年龄:%d\n", pstu[i].age);
printf("成绩:%d\n", pstu[i].score);
}
return 0;
}
int main(void)
{
struct student s[100]; // 最多
int curlen = 0; // 实际
curlen = GetAllStuInfo(s, 100);
PutAllStuInfo(s, curlen); // 接多少输出多少
return 0;
}