1、枚举
1】定义,类似于宏,枚举就是编译阶段的宏
2】枚举类型里面的值无法修改
3】如果定义了枚举类型没有赋值,默认第一项为0,后续依次加1
4】如果赋了值,就为实际赋的值
2、回调函数
回调函数以及函数指针数组的应用(此问题是输入学生信息,根据出生日期排序。具体大可不必如此复杂,写出以下代码是为了练习)
#include <stdio.h>
typedef int (*p)(int, int); // 定义p为int (*)( int, int )类型的函数指针。表示其函数的返回值是int ,参数是两个int类型
// 定义一个结构体数组来存储学生基本信息
typedef struct student
{
char name[10];
int born;
} stu;
// 定义一个年份比较函数
int year_cmp(int a, int b)
{
a = a / 10000;
b = b / 10000;
return a - b;
}
// 定义一个月份比较函数
int month_cmp(int a, int b)
{
a = (a / 100) % 100;
b = (b / 100) % 100;
return a - b;
}
// 定义一个日份比较函数
int day_cmp(int a, int b)
{
a = a % 100;
b = b % 100;
return a - b;
}
// 定义一个函数来排序
void sort(stu *arr, int n, p *cmp)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
int result = cmp[j](arr[j].born, arr[j + 1].born);
if (result > 0)
{
stu temp;
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
int main()
{
// 定义一个结构体数组用于存储学生信息
stu stu[5];
for (int i = 0; i < 3; i++)
{
printf("please enter the number %d student's message:\n", i);
scanf("%s", stu[i].name);
while (getchar() != '\n')
; // 清空输入缓冲区
scanf("%d", &stu[i].born); // 注意此处传入 &
}
// 定义比较函数指针数组
p cmp[] = {year_cmp, month_cmp, day_cmp};
sort(stu, 3, cmp);
for (int i = 0; i < 3; i++)
{
printf("%s %d\n", stu[i].name, stu[i].born);
}
return 0;
}
3、结构体
1、定义
结构体是一种自定义的数据类型,用于将不同类型的数据组合在一起形成一个新的数据类型(相当于int这些数据类型,int这些如何定义变量,结构体便如何定义)。它允许我们在一个单独的变量中存储和操作多个相关的数据。
结构体内的各个元素的类型不同,且是这些不同类型的元素的集合。虽然有点像数组的定义(同种元素的有序集合),但结构体数组和结构体是两个意义。在理解结构体的时候还是把它当作一个特殊的数据类型好一些(如特殊的int?)或者说,一个对象的大属性,大属性内包含小属性。即一个人是学生,而学生有不同的姓名,性别,分数等。即定义一个学生数据类型。学生具有各个小属性。而结构体数组应该表示具有同种类型(学生数据类型)的集合。
结构体的作用是将相关的数据组织在一起,以便更方便地管理和操作这些数据。它可以提高代码的可读性和可维护性,使得程序的逻辑更清晰。
结构体的具体应用方向很广泛,常见的应用包括:
- 数据库中的表结构:可以使用结构体来表示数据库中的表结构,每个结构体成员对应表中的一个字段。
- 图形界面编程:可以使用结构体来表示窗口、按钮、文本框等界面元素的属性和状态。
- 游戏开发:可以使用结构体来表示游戏中的角色、道具、地图等元素的属性和状态。
- 网络编程:可以使用结构体来表示网络协议中的数据包结构。
2、结构体的声明
/*struct 结构体类型名{
数据类型 成员1;
数据类型 成员2;
...
};//注意结束的引号
*/
struct student{
char name[32];
int score;
int agr;
};//注意结束的引号
//定义:
struct student stu;
//初始化:
struct student stu = {"bob", 88 , 18 };
//使用:
// 结构体变量名.成员名;
通过typedef 定义结构体可以使代码更加简洁
#include <stdio.h>
typedef struct student
{
char name[32];
int age;
double socore;
} stu;//注意添加;
int main()
{
stu A = {"Alice", 22, 99.99};
stu B;
strcpy(B.name,"Bob");//注意,此处的字符串数组赋值不能用等号赋值。
printf("%s", A.name);
return 0;
}
注意字符串的赋值问题:
在C语言中,字符数组的初始化有两种方式:静态初始化和动态初始化。
静态初始化是在定义数组时直接给数组赋初值,例如char name[20] = {"kitty"};。这种方式只能在定义数组时使用,不能在后续的赋值操作中使用。
动态初始化是在定义数组后通过字符串拷贝函数strcpy来给数组赋值,例如strcpy(student1.name, "bob");。这种方式可以在任何时候使用,包括定义后的赋值操作。
所以,对于结构体中的字符数组成员student1.name,不能使用student1.name={"bob"};这样的方式直接赋值,因为这种方式只适用于静态初始化。需要使用strcpy函数来进行动态初始化。
3、结构体的输入与输出
在使用scanf向结构体输入字符的时候,需注意scanf的脏字符问题。因为键盘输入的\r为换行字符,其会被保留
printf("please enter student name:\n");
gets(student1.name);
printf("please enter student age:\n");
scanf("%d", &student1.age);
printf("please enter student sex:\n");
scanf("%c", &student1.sex);
printf("please enter student score:\n");
scanf("%f", &student1.score);
输出:
asd
please enter student age:
22
please enter student sex:
please enter student score:
99
Name: asd
Age: 22
Sex:
Score: 99.0
可以看到脏字符\r在缓存区被sex的scanf函数读取。导致sex无法正常输出
解决方法一:getchar()吃掉脏字符
printf("please enter student name:\n");
gets(student1.name);
printf("please enter student age:\n");
scanf("%d", &student1.age);
printf("please enter student sex:\n");
getchar(); // 消耗掉之前的换行符
scanf("%c", &student1.sex);
printf("please enter student score:\n");
scanf("%f", &student1.score);
解决方法二:在 scanf("%c", &student1.sex);中的%c之前加一个空格
在scanf函数中,空格字符(空格、制表符、换行符等)会被视为分隔符,用于分隔输入的不同部分。上一个scanf输入后,\r被留在缓存区,被下一个scanf函数作为字符读取。当在scanf函数之前加一个空格字符时,它会告诉scanf函数跳过之前的所有空格字符,然后读取下一个非空格字符作为输入。