jquery 手机网站开发,网站服务器做下载链接,百度官网下载安装免费,辽宁建设工程文章目录 (八) 指针0.概念1.指针基础(1)指针的声明(2)指针的两个基本操作①取地址运算符 ②解引用运算符 * (3)野指针①野指针②空指针③指针变量的赋值 vs 指针变量指向对象的赋值 (4)指针的应用①指针作为参数进行传递②指针作为返回值③拓展#xff1a;栈帧 (5)常量指… 文章目录 (八) 指针0.概念1.指针基础(1)指针的声明(2)指针的两个基本操作①取地址运算符 ②解引用运算符 * (3)野指针①野指针②空指针③指针变量的赋值 vs 指针变量指向对象的赋值 (4)指针的应用①指针作为参数进行传递②指针作为返回值③拓展栈帧 (5)常量指针、指针常量(6)传入参数、传出参数 2.指针与数组(1)指针的算数运算加法、减法、比较(2)指针和数组的关系①用指针处理数组指针代替索引②数组可以退化为指向数组第一个元素的指针③指针也支持取下标运算 (3)*与的组合(4)指针支持的操作 3.指针的高级应用(1)动态内存分配①内存分配函数malloc、calloc、realloc②空指针 NULL、通用指针③动态分配数组vector (2)释放内存空间free(3)动态分配结构体(4)二级指针指向指针的指针(5)函数指针指向函数的指针 (九) 字符串0.总纲(1)C字符串的遍历①数组下标②指针③注意④遍历字符串的三个效率级别 1.字符串常量 (字符串字面值)(1)概念(2)字符串字面值的三种书写方式(3)内存模型(4)字符串字面值支持的操作 2.字符串变量(1)声明字符串变量并赋初始值(2)字符数组 vs 字符指针 3.读 / 写字符串(1)读使用scanf和gets读字符串①scanf %s读取一个单词②gets()从stdin中读取一整行数据存入字符数组。并将\n替换为\0③fgets() (2)写使用printf和puts写字符串①printf %s②puts() 4.C字符串的操作C语言字符串库(1)strlen①惯用法遍历字符串 / 搜索字符串末尾 (2)strcpy①strcpy(s1, s2)②strncpy(s1, s2, count)③惯用法复制字符串 (空字符也复制) (3)strcat①strcat(s1, s2)②strncat(s1, s2, count) (4)strcmp 5.字符串数组(1)二维字符数组(2)字符指针数组 6.命令行参数7.练习(1)逆序输出字符串(2)回文字符串 (十) 结构体、枚举1.结构体 struct(1)结构体变量的声明和初始化(2)结构体的内存模型(3)结构体的操作获取成员、赋值(4)右箭头运算符- 的由来(5)给结构体起别名(6)匿名结构体(7)练习 2.枚举 enum (八) 指针
0.概念
①计算机最小寻址单位字节 ②变量的地址变量首字节的地址 ③指针就是地址 ④指针变量存储地址值的变量 ⑤野指针不知道指向哪个对象的指针 ⑥空指针不指向任何对象的指针 ⑦常量指针和指针常量 ⑧传入参数和传出参数 ⑨通用指针类型 void* 1.指针基础
(1)指针的声明
1.int *p 或 int* p *说明了p是指针
2.变量名p类型int*
3.注意事项声明指针变量时需要指定它指向对象的类型 int是指向对象的类型①说明对象所占内存大小 ②如何解释那片内存空间 (说明了对象的类型) (2)指针的两个基本操作
①取地址运算符 ②解引用运算符 * 0.示例 i直接访问逻辑上访问内存一次 *p间接访问逻辑上访问内存两次
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hint main(void) {int i 1;int* p i;printf(*p %d\n, *p);*p 2; //*p 是 i 的别名,有读写权限printf(i %d\n, i);return 0;
}(3)野指针
①野指针
(1)野指针不知道指向哪个对象(哪块数据)
(2)对野指针进行解引用是未定义行为
(3)野指针的两种表现形式
int* p; //1.不初始化
int* q 0xABCD; //2.用一个整数赋值(4)正确地给指针变量赋值的两种方式
int* p i; //1.
int* q p; //2.
p NULL; //2.②空指针
(1)空指针(NULL)不指向任何对象的指针不指向任何有效的内存地址 (2)不能对空指针进行解引用 ③指针变量的赋值 vs 指针变量指向对象的赋值
①p q ②*p *q (4)指针的应用
①指针作为参数进行传递
好处在被调函数中可以修改主调函数中变量的值解引用
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hvoid swap(int* p, int* q) {int temp *p;*p *q;*q temp;
}int main(void) {int a 3, b 4;printf(a %d, b %d\n, a, b);swap(a, b);printf(a %d, b %d\n, a, b);return 0;
}②指针作为返回值
教训不要返回指向当前栈帧区域的指针 (因为返回以后该栈帧就出栈了变量被销毁) ③拓展栈帧
栈帧 esp、ebp
栈帧里存储的是函数调用相关的信息形参、局部变量、返回地址 (5)常量指针、指针常量 const本质限制变量的写权限 这里const是限制指针变量p的写权限 1.不加const正常情况 2.const int* p (pointer to const) 对内存1有写权限但对内存2没有写权限。 即可以修改指针的指向但不能通过 *p 对所指变量的值进行修改。(但变量i自身可修改) 3.int* const p (const pointer) 对内存1没有写权限但对内存2有写权限。 即不能修改指针的指向但可以通过 *p 修改所指对象的值 4.const int* const p (const pointer to const) 对内存1和内存2都没有写权限。 即不能修改指针的指向、不能通过 *p 改写变量的值 (6)传入参数、传出参数
1.传入参数const int* p 在函数里面不能够通过指针变量修改指针指向的对象 2.传出参数int* p 在被调函数中可通过指针变量修改主调函数中指向的对象的值可替代返回值来用 (C语言返回值只能返回一个值但指针修改可修改多个值即通过传出参数可代替多个返回值) 传入参数和传出参数指的都是指针变量 2.指针与数组
(1)指针的算数运算加法、减法、比较
1.指针的加法 ①指针 整数指针向右偏移几个单位
p p3; //指针向右偏移3个单位2.指针的减法 ①指针减去整数n代表指针向左偏移n个单位
p p-3; //指针向左偏移3个单位②两个指针相减结果为一个整数相隔几个单位
int n p-q;3.指针的比较运算 (2)指针和数组的关系
概念 数组是一片连续的内存空间并被划分为一个个大小相等的小空间 指针与另一个对象进行关联 ①用指针处理数组指针代替索引
指针处理数组(早期C语言) 现代的编译器会把for顺序处理在编译层面翻译成第一种写法避免了乘法运算 ②数组可以退化为指向数组第一个元素的指针
在必要的时候数组可以退化为指向数组第一个元素的指针 退化arr[0] 就可以写为 arr
数组会退化为指针的情景 ①数组作为参数传递 fun(arr) ②数组给指针变量赋值时int* p arr (数组在赋值表达式的右边即数组进行赋值运算时作为右值) ③数组参与算术运算 arr3 ③指针也支持取下标运算
p[i] 等价于 *(pi) p[i] 等价于 *(pi)等价于 *(ip) 等价于i[p]。 故,防御性编程i[arr] #define _CRT_SECURE_NO_WARNINGS
#include stdio.hint main(void) {int arr[5] { 0,1,2,3,4 };//1.指针也支持取下标运算 p[i]int* p arr;for (int i 0; i 5; i) {printf(%d , p[i]);}printf(\n);//2.防御性编程 i[arr]for(int i 0; i 5; i){//printf(%d , arr[i]); //arr[i] 等价于 *(arri)printf(%d , i[arr]); //i[arr] 等价于 *(iarr)}printf(\n);return 0;
}(3)*与的组合
表达式表达式的值副作用① *p 或 *(p)*pp自增② (*p)*p*p自增③ *p 或 *(p)*(p1)p自增④ *p 或 (*p)(*p)1*p自增 ①*p 或 *(p) 表达式值为 *p副作用是 p自增 (最常见) ②(*p) 表达式值为 *p副作用是 *p自增 ③*p 或 *(p)表达式值为 *(p1)副作用是p自增 ④*p 或 (*p)表达式值为 (*p)1副作用是 *p自增 (4)指针支持的操作
①解引用 *通过指针变量获取它指向的对象 ②算数运算加整数、减整数、减指针、自增、自减、比较运算(、!、、、、) ③取下标 [] 3.指针的高级应用
(1)动态内存分配
1.为什么要在堆上分配空间、为什么需要动态内存分配(why) ①栈帧的大小是在编译期间确定的栈空间不能存放动态大小的数据如vector ②栈空间比较小主线程8MB其他线程2MB。所以栈上不能存很大的数据。 ③每个线程都有自己的栈多线程共享的数据最好不要存在栈上应该存在堆上 ①内存分配函数malloc、calloc、realloc
1.如何申请堆空间 (How) 答使用内存分配函数
2.头文件#include stdlib.h (1)malloc mallocmemory allocate内存分配函数
分配连续内存的大小
int* p malloc(sizeof(int) * 100);(2)calloc clear allocate申请空间并清零(全部赋值为0)。(空指针NULL值也是0)
calloc为 num_elements 个大小为 element_size 的元素分配内存空间并将所有的位初始化为 0。如果函数成功它将返回一个指向已分配内存的指针。如果失败它将返回 NULL。
int* p calloc(个数, sizeof(类型));(3)realloc 调整先前分配的内存块大小。如果重新分配内存大小成功返回新内存块的指针否则返回空指针。(旧内存块不会被释放) 缩容是直接截断。尽可能地原地扩容扩充的内存是未初始化的
void* reallloc(void* ptr, size_t size);②空指针 NULL、通用指针
1.NULL
2.void * p 通用指针 作用C语言中通用指针 void * 可以与其他任意类型的指针 相互转化。也即 void * 指向对象的类型还不确定不能直接操作(解引用、自增、加法等)通用指针。 ③动态分配数组vector
1.动态数组的实现
typedef int E;typedef struct{E* elements; //指向堆空间的数组int capacity; //容量int size; //实际个数
} Vector;2.跨文件编写程序 依赖接口不要依赖具体的实现 (因为实现是变化的接口一般是固定的、稳定的) 接口*.h、Interface、抽象类 接口中存放类型定义和API的声明 3.头文件 (1)头文件中存放类型的定义、API的声明 API的声明可以给用户使用的。 但实现时不希望用户直接使用的函数(实现)就不要放到头文件中
(2)两种头文件 自己写的头文件搜索路径当前目录 (若找不到) - 系统头文件包含目录 搜索路径系统头文件包含目录 (3)依赖关系图 (2)释放内存空间free
1.垃圾(garbage)不可再被访问的内存块 内存泄漏(memory leak)程序中存在垃圾 2.垃圾回收器 (1)①有垃圾回收器的语言Java、Python、Go ②手动管理垃圾的语言C、C、rust
(2)①垃圾回收器的特点减轻程序员的负担、但引入了不确定因素。清除垃圾时会stop the world不适合写实时系统。[实时系统在某个确定的时间内完成某个任务] ②没有垃圾回收器 Cfree Cdelete、析构函数、智能指针、RAII Rust所有权机制 3.free的问题
void free(void *ptr);悬空指针(野指针的一种) 对堆上内存释放一次后p就变成了悬空指针 (1)double free free两次
(2)use after free free后再使用
(3)忘记free 造成内存泄露
结论当堆上的数据不再使用时应该有且只释放一次 (3)动态分配结构体
见链表 (4)二级指针指向指针的指针
Q传一级指针还是传二级指针 A想修改哪个变量就传那个变量的地址(指针) ①想修改指针指向的对象传一级指针 ②想修改指针的指向 (修改指针变量的值)传二级指针 (5)函数指针指向函数的指针
1.声明函数指针变量
int (*p) (int, int); //参数类型是(int,int), 返回值类型是int区分声明函数
int* p2(int, int);2.初始化 / 赋值foo、foo
int (*p1)(int, int) foo;
int (*p2)(int, int) foo;3.通过函数指针调用函数
p(a,b);
(*p)(a,b);4.作用 / 应用场景 (1)函数式编程 (传递函数返回函数) C语言通过函数指针支持函数式编程 好处分解任务解耦合 (2)编写非常通用的函数 (功能非常强大的函数)如qsort()
举例qsort函数
函数指针调用的函数称为钩子函数如cmp( ) 函数指针实现了分解任务解耦合。(将排序和比较分开了。若没有函数指针调用函数则比较的逻辑要写死在qsort中类型要固定无法实现通用的功能) (九) 字符串
0.总纲
①C语言没有字符串类型 ②C语言中的字符串依赖字符数组存在 (字符数组最后一个存\0才是C字符串) ③C语言中的字符串是一种逻辑类型 (1)C字符串的遍历
在C语言中字符串是一系列以空字符‘\0’结尾的字符数组。可以通过迭代每个字符来遍历字符串有以下两种常用的方式
①数组下标
1.使用数组下标
#include stdio.hint main() {char str[] Hello, World!;for (int i 0; str[i] ! \0; i) {printf(%c\n, str[i]);}return 0;
}②指针
2.使用指针
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hint main(void) {char str[] Hello;char* p str;while (*p ! \0) {printf(%c, *p);p;}return 0;
}完整代码
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hint main(void) {char str[] Hello,;char* p str;while (*p ! \0) {printf(%c, *p); };char str2[] World!;for (int i 0; str2[i] ! \0; i) {printf(%c, str2[i]);}return 0;
}③注意
①C语言中的字符串以空字符\0结尾 ②C语言中求字符串的长度需要从头遍历是O(n)的时间复杂度。 length是O(1)的时间复杂度 strlen是O(n)的时间复杂度 ④遍历字符串的三个效率级别
1.最糟糕的写法
for (int i 0; i strlen(str); i) { ... }因为strlen()本身就是O(n)的复杂度又嵌套在for里使得这个遍历字符串的时间复杂度达到了O(n²) 2.稍微好一些的写法
int len strlen(str);
for (int i 0; i len; i) { ... }3.比较好的写法数组下标 ‘\0’
for (int i 0; str[i] ! \0; i) { ... }4.最好的写法指针操作字符串
char* p str;
while (p){...p;
}1.字符串常量 (字符串字面值)
(1)概念
字符串常量(字符串字面值)表示双引号括起来的字符序列 (2)字符串字面值的三种书写方式
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hint main(void) {//字符串字面值的三种书写方式//1.最普通的书写方式printf(I love xixi -- From peanut\n);//2.换行 (但是换行后不忽略空白字符)printf(I love xixi \-- From peanut\n);//3.字符串拼接printf(I love xixi -- From peinut\n);return 0;
}当两个或更多个字符串字面值相邻时 (仅用空白字符分割)编译器会把 它们合并成一个 举例模拟 输出图形
//打印菱形
printf( *\n * *\n * *\n* *\n * *\n * *\n *\n );传统的两层for循环可读性差性能低。 (3)内存模型
1.字符串字面值存放在代码段不可被修改。 代码段存放指令、字符串字面值 2.C语言中字符串以\0结尾 void 空类型 (没有值) \0 空字符 (C字符串结束标志) NULL 空指针 (不指向任何对象) 空字符串 (4)字符串字面值支持的操作
常量数组支持的操作字符串字面值都支持。可以把字符串字面值看作是常量数组
字符串内容可作为数组名支持取下标运算
char* p ABC 1; //ABC是数组名,进行算术运算时退化为首元素的指针,1就是向右偏移1个单位
printf(%c\n,*p); //输出B//十六进制转换
char digit_to_hex(int dight){return 0123456789ABCDEF[dight];
}2.字符串变量
(1)声明字符串变量并赋初始值
两种方式第二种是第一种的语法糖
//声明字符串变量并赋初始值
char str0[] { H,e,l,l,o }; //字符数组
char str1[] { H,e,l,l,o,\0}; //字符串: 数组的初始化式 { }
char str2[] Hello; //字符串: 语法糖,Hello是数组初始化式的简写形式建议 ①如果初始化字符数组用数组的初始化式{H,e,l,l,o,\0} ②如果初始化字符串用双引号语法糖Hello char类型0值就是空字符\0 例
char s[10] {H,e,l,l,o,\0}①字符数组长度10 ②字符串长度5 ③字符串占用的空间6 (2)字符数组 vs 字符指针
char str[] hello; //hello:数组的初始化式
char* p hello; //hello:字符串字面值3.读 / 写字符串
(1)读使用scanf和gets读字符串
①scanf %s读取一个单词
scanf(%s,str); //数组名退化为指针,就是地址。不需要加取地址运算符char str[MAX_SIZE];scanf(%s, str); //scanf %s会忽略前置空白字符,遇到空白字符停止
printf(%s\n, str);(1)%s的匹配规则忽略前置空白字符读取字符填入字符数组遇到空白字符结束。
(2)缺点 ①不能够存储空白字符 ②不会检查数组越界 (读多了超过了数组长度数据覆盖了数组后面的内存空间) ②gets()从stdin中读取一整行数据存入字符数组。并将’\n’替换为’\0’
1.匹配规则 gets()不会忽略前置空白字符一次读取一行遇到空白字符不结束直到遇到换行符\n才结束并将’\n’替换为’\0’
2.缺点不会检查数组越界。(若要读取的字符串长度超过了字符数组的长度也会照样写内存覆盖数组后面的内存的数据造成数组越界) ③fgets()
fgets(str,sizeof(str),stdin); //str,数组长度,从哪里读入注意事项 ①fgets()会检查数组越界 (对比第二个数值) ②会保存换行符’\n’并在后面添加’\0’。以\n\0结尾 (2)写使用printf和puts写字符串
①printf %s
输出一个字符串。(从头输出到空字符结束空字符\0标志着字符串的结束)
%.ps精度p最多输出p个字符
char str[] Hello world;
printf(%s\n, str);
printf(%.5s\n, str); //%.ps②puts()
①puts()输出一个字符串 ②puts()效率高于printf不用处理格式化输出
printf(%s\n,str);
puts(str); //两种写法等价puts()会自动添加换行符。puts()效率更高4.C字符串的操作C语言字符串库
头文件 string.h (1)strlen
strlen()求字符串的长度 ①惯用法遍历字符串 / 搜索字符串末尾
while(*p \0){p;
}②sizeof() 和 strlen() 的区别
char str1[10] abc;
char str2[ ] abc;printf(sizeof(str1) %d\n, sizeof(str1)); //10
printf(sizeof(str2) %d\n, sizeof(str2)); //4
printf(strlen(str1) %d\n, strlen(str1)); //3
printf(strlen(str2) %d\n, strlen(str2)); //3(2)strcpy
①strcpy(s1, s2)
strcpy(str,Hello);②strncpy(s1, s2, count)
strncpy(str,Hello world,MAXLINE-1);
str[MAXLINE-1] \0;③惯用法复制字符串 (空字符也复制)
while(*s1 *s2);完整版
char* p s1;
while(*s1 *s2);
return p;(3)strcat
①strcat(s1, s2) concatenate v.连接 1.strcat也可能数组越界 ②strncat(s1, s2, count)
strcat(s1, world\n), MAXLINE - strlen(s1) -1); //1 for \0
s1[MAXLINE - 1] \0; //记不住它到底会不会自动添加空字符就一律手动添加一次(4)strcmp
strcmp返回值是s1 - s2 5.字符串数组
字符串数组即字符数组的数组即二维字符数组
(1)二维字符数组 里的是一维字符数组的初始化式 弊端 ①空间浪费字符串之间长度差异大短的后面就要存很多’\0’造成空间浪费 ②不灵活如交换字符串、字符串进行排序 (2)字符指针数组
字符指针数组来存储字符串数组 里的是字符串字面值 缺点多了一个存储字符数组
优点 ①节省了空间 ②灵活
#include stdio.hint main(void) {//planets是数组存储的元素是 char*, 字符指针 ,即指向字符的指针char* planets[] { Mercury, Venus, Earth, Mars,Jupiter, Saturn, Uranus, Neptune };char* p Edward; planets[2] p; int len strlen(planets);for (int i 0; i len; i) {printf(%s\n, planets[i]);}return 0;
}6.命令行参数
1.命令行参数是什么 操作系统调用可执行程序时可以给它(main函数)传递的参数
2.要接收命令行参数要修改main函数的参数
int main(int argc, char* argv[]) {//argc:argument count,命令行参数的个数//argvargument vector,命令行参数,字符串//argv[0]是第一个命令行参数一般为可执行程序的路径程序名printf(argc %d\n, argc);printf(argv[0] %s\n\n, argv[0]); for (int i 0; i argc; i) {printf(argv[%d] %s\n, i, argv[i]);}return 0;
}3.命令行参数的转换 sscanf是string scanf
int main(int argc, char* argv[]) {int n;float f;sscanf(argv[1], %d, n);sscanf(argv[2], %f, f);for (int i 0; i argc; i) {printf(argv[%d] %s\n, i, argv[i]);}return 0;
}4.如何在VS中设置命令行参数 项目[右键]→属性→调试→命令参数→[以空格间隔]→应用→确定
5.命令行参数和stdin的区别 命令行参数在程序执行之前stdin在程序执行中
6.命令行参数有什么作用 ①编写通用的程序 cp a.txt b.txt ②改变程序的行为 ls -l (传递不同的参数程序展示不同的行为) 7.练习
(1)逆序输出字符串
CDay07第一题 (2)回文字符串
CDay07第二题 (十) 结构体、枚举
1.C语言最重要的三个组成部分函数、指针、结构体
2.C语言中的聚合变量 ①数组 (同类的元素) ②结构体 (不同类的成员)
3.对象 ①属性静态数据 ②方法行为
4.C语言结构体中只有属性没有方法。 但是C可以通过指针实现类似方法的功能 1.结构体 struct
(1)结构体变量的声明和初始化
1.定义结构体 结构体类型是自定义类型
struct student{int id;char name[25];int age;char gender;int chinese;int math;int english;
};2.声明并初始化变量
struct student s1 {1, xixi, F, 100}; //按位置赋值
struct student s2 {2, peanut, M}; //未初始化的成员,默认为0值(2)结构体的内存模型
(1)一片连续的内存空间 (2)按声明的顺序依次存放每一个成员 (3)成员之间 (在结构体变量的中间或后面)可能会进行填充为了内存对齐 四个字节四个字节的传输时如果不对齐则int会被分割开要读两次。 对齐的目的是为了减少读的次数更快地访问数据 (3)结构体的操作获取成员、赋值
(1)获取成员 . (2)赋值
s2 s1;赋值的本质 是 内存空间的复制。 在结构体很大时复制结构体的开销很大。 传递参数和返回值时都会复制结构体考虑只传递结构体的指针。 数组不支持赋值运算而结构体支持赋值运算 (4)右箭头运算符- 的由来
传递或返回一个结构体时会导致结构体的复制。当结构体很大时会增加很多开销。 所以C程序员往往会传递结构体的指针。 现在s是指针*s才是结构体。则 s. 就要写成 (*s). 先解引用 (结构体指针解引用为结构体)再获取成员 为了简便书写将 (*s). 写作 s- (语法糖)
格式: 指向结构体的指针 - 结构体的成员完整代码
#define _CRT_SECURE_NO_WARNINGS
#include stdio.hstruct student{int id; //4char name[25]; //25char gender; //1int chinese; //4int math; //4int english; //4
};void print_stu_info(const struct student* s) {/*printf(%d %s %c %d %d %d\n,(*s).id, (*s).name, (*s).gender,(*s).chinese,(*s).math,(*s).english);*///语法糖: 用 s- 代替 (*s).printf(%d %s %c %d %d %d\n, s-id, s-name, s-gender,s-chinese, s-math, s-english);
}int main(void){//声明并初始化变量struct student s1 { 1, xixi, F, 100, 100, 100 };struct student s2 { 2,peanut, M };print_stu_info(s1); //s1是结构体, s1是结构体的指针print_stu_info(s2);return 0;
}(5)给结构体起别名
typedef 类型 别名;typedef struct student{int id; char name[25]; char gender; int chinese; int math; int english;
} Student;//请不要给指针类型起别名! 如下文的 *pStudent
typedef struct student{int id; char name[25]; char gender; int chinese; int math; int english;
} Student, *pStudent;(6)匿名结构体
匿名结构体 没有标签需要搭配 typedef 来形成新名字
typedef struct { //匿名结构体int id; char name[25]; char gender; int chinese; int math; int english;
} Student;(7)练习
结构体指针 Student* p 的作用避免复制整个结构体 结构体指针数组 Student* pstudents[5] 的作用通过交换指针数组中指针的顺序堆学生按总成绩进行排序避免了对结构体本身进行排序(移动)减少了开销 #define _CRT_SECURE_NO_WARNINGS
#include stdio.htypedef struct student {int number;char name[25];int chinese;int math;int english;
} Student;void print_stu_info(const Student* p) {printf(%d %s %d %d %d\n, p-number, p-name, p-chinese, p-math, p-english);
}void highest(Student stu[], int n) {int max_chinese stu[0].chinese, max_math stu[0].math, max_english stu[0].english;int index_chinese 0, index_math 0, index_english 0;for (int i 1; i 5; i) {if (stu[i].chinese max_chinese) {max_chinese stu[i].chinese;index_chinese i;}if (stu[i].math max_math) {max_math stu[i].math;index_math i;}if (stu[i].english max_english) {max_english stu[i].english;index_english i;}}printf(语文最高分的同学的信息:);print_stu_info(stu[index_chinese]);printf(数学最高分的同学的信息:);print_stu_info(stu[index_math]);printf(英语最高分的同学的信息:);print_stu_info(stu[index_english]);
}void average(Student stu[], int n) {float sum_chinese 0, sum_math 0, sum_english 0;for (int i 0; i n; i) {sum_chinese stu[i].chinese;sum_math stu[i].math;sum_english stu[i].english;}printf(语文平均分:%.1f\n, sum_chinese / n);printf(数学平均分:%.1f\n, sum_math / n);printf(英语平均分:%.1f\n, sum_english / n);
}int sum_score(const Student* p) {return p-chinese p-math p-english;
}void Bubble_des(Student* A[], int n) {for (int i 0; i n - 1; i) {for (int j 0; j n - 1 - i; j) {if (sum_score(A[j]) sum_score(A[j 1])){ //对于降序条件改为小于Student* temp A[j];A[j] A[j 1];A[j 1] temp;}}}
}int main(void) {Student students[5]; //结构体数组,大小为5for (int i 0; i 5; i) {scanf(%d%s%d%d%d, students[i].number, students[i].name,students[i].chinese, students[i].math, students[i].english);}printf(\n);highest(students, 5);printf(\n);average(students, 5);printf(\n);//结构体 指针数组Student* pstudents[5] { students, students1, students2 ,students3, students4 };Bubble_des(pstudents, 5);for (int i 0; i 5; i) {print_stu_info(pstudents[i]);}return 0;
}2.枚举 enum
1.枚举类型用来表示离散值如 类型和状态。枚举类型是整数类型。 2.定义枚举类型
// 定义枚举类型
enum Suit{// 罗列枚举值DIAMONDS, //0 默认从0开始HEARTS 4, //4SPADES 10, //10CLUBS //11 递增
};3.给枚举类型起别名
typedef enum{DIAMONDS,HEARTS,SPADES,CLUBS
} Suit;