前言
说真的,在云顶书院的四周适应期简直太快太快了。
就感觉好像还没开始学啥呢,但转头一看,之前对于C语言还是萌新的我已经学会了好多好多了......
C语言的基本语法网上都有,在这就不再整理了,那就写点儿不一样的吧!
作为强迫症 在这几周的编码过程中时时刻刻都想着优化自己的代码:怎么写更快、更美、更易读、更可续。甚至有的代码只写了1个小时,但优化它的时间甚至都接近了10个小时。
在这儿主要是想梳理一些近期写过/遇到的比较有趣的代码
Amazing code
Let's enjoy it!
通过数组调用函数
在完成本周作业时,需要一个随机生成+-*/的功能,但是对生成的数字都有一定的范围要求(1~100),那么在生成随机数后 首先想到的是先把四个运算生成器封装成函数,再返回是否答对,但是到了这里,使用连续的if-else就显得不够那么“优雅”。于是就想到了将四个函数放到一个数组里,由于函数名并不能通过整数型和字符型这些来表示,于是就想到了指针:将函数的地址储存到一个长度为4的数组中。调用时就可以先使用随机数作为数组索引进行调用。
具体实现方式如下:
int (*exam_container[4]) (int i) = {add_exam, sub_exam, mul_exam, div_exam};
其中第一个int表示函数的返回值是int类型,*表示该数组储存的为指针,int i表示这四个函数的形参都是int i,后面大括号里面则是定义的四个函数的名称(注意不加括号)
接着又发现了一个弊端,以加法生成器add_exam()为例
//加法
int add_exam (int i) {
//加法题目生成采用答案倒推法,避免使用while过多消耗系统资源
int input;
int res = random (1, 100);
int add2 = random (1, res - 1); //保证add1 > 0
int add1 = res - add2;
printf ("%d + %d =", add1, add2);
scanf ("%d", &input);
exam_log[i][0] = add1;
exam_log[i][1] = '+';//自动转化为ASCII码
exam_log[i][2] = add2;
exam_log[i][3] = res;
exam_log[i][4] = input;
if (input == res) {
return 10;
} else {
return 0;
}
}
其中int类型exam_log负责储存本次考试每道题的详情,这段代码显然在四个函数里是重复的,scanf和input==res同样也是。
这里就需要用函数返回一个数组,交由上层函数统一进行调用,于是就用到了指针函数: C 从函数返回数组
再经过一番修改,函数就变成这样了
//加法
int *add_exam () {
static int outcome[4];
//加法题目生成采用答案倒推法,避免使用while过多消耗系统资源
int res = random (1, 100);
int add2 = random (1, res - 1); //保证add1 > 0
int add1 = res - add2;
printf ("%d + %d =", add1, add2);
outcome[0] = add1;
outcome[1] = '+';//相当于返回的ASCII码
outcome[2] = add2;
outcome[3] = res;
return outcome;
}
由于函数变为了指针函数,且无需参数了,那么定义储存函数指针的数组就变成这样了
int * (*exam_container[4]) () = {add_exam, sub_exam, mul_exam, div_exam};
很恐怖对不对?
完整代码待作业提交结束后奉上~
//written by moyi
//2022-11-04
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
char data[100][3][20];
int count = 0;
int exam_log[10][5];
char user_id[50];
int login();
void initialize();
void read_file();
void write_file (char *user_id, int mark, int time);
int random (int start, int end);
int random_except (int start, int end, int except_num);
int print_info (char *user_id);
void exam (char *user_id);
void menu();
int *add_exam ();
int *sub_exam ();
int *mul_exam ();
int *div_exam ();
//把四种运算生成器放到数组里
//前面的*代表里面函数返回指针(这个试了好几分钟才发现的问题)
//后面的星号代表函数的地址
int * (*exam_container[4]) () = {add_exam, sub_exam, mul_exam, div_exam};
int main() {
initialize();
menu();
return 0;
}
void menu(){
while (1) {
char input[50];
printf ("\nMenu:\n1.开始测试\n2.检查分数\n3.退出\n\n请输入指令(1/2/3):");
scanf ("%s", input);
if (!strcmp (input, "1") ) {
exam (user_id);
} else if (!strcmp (input, "2") ) {
if(!print_info (user_id)){
printf("你没考过试!\n");
}
} else if (!strcmp (input, "3") ) {
return;
} else {
system ("cls");
printf ("看看你写的啥!\n\n");
}
}
}
//初始化
void initialize(){
srand ( (unsigned int) time (NULL) );
while(!login()){
printf("\n格式错误!请重新输入!\n");
}
}
//登录
int login(){
printf ("What is your ID:");
scanf ("%s", user_id);
if (strlen (user_id) != 6) {
return 0;
}
int i;
for (i = 0; i < 6; i++) {
if (i < 2) {
if (user_id[i] > 'Z' || user_id[i] < 'A') {
return 0;
}
} else {
if (user_id[i] > '9' || user_id[i] < '0') {
return 0;
}
}
}
system("cls");
printf ("Login success!\nHello:%s\n", user_id);
return 1;
}
//读文件
void read_file() {
//r:文件不存在会报错
//w+:文件被清空
//a:只写在最后面
//a+:读写,不会覆盖文件,没有文件也可以自动创建
FILE *fp = fopen ("record.txt", "a+");
char row[80];
char *token;
int i = 0;
count = 0;//读取前重置计数器
while (fgets (row, 80, fp) != NULL) {
count++;
token = strtok (row, ",");
int j = 0;
while (token != NULL) {
if (j == 2) {
token = strtok (token, "\n");
}
strcpy (data[i][j], token);
token = strtok (NULL, ",");
j++;
}
i++;
}
fclose (fp);
}
//写入文件
void write_file (char *user_id, int mark, int time) {
FILE *fp = fopen ("record.txt", "a"); //a:追加到文件结尾,节省IO资源
if (fp == NULL) {
fprintf (stderr, "fopen() failed.\n");
exit (EXIT_FAILURE);
}
fprintf (fp, "%s,%d,%d\n", user_id, mark, time);
fclose (fp);
return;
}
int random (int start, int end) {
return rand() % (end - start + 1) + start;
}
//生成随机数时不包含某数
int random_except (int start, int end, int except_num) {
int tmp;
do {
tmp = random(start, end);
} while (tmp == except_num); //注意是取等才循环
return tmp;
}
//加法
int *add_exam () {
static int outcome[4];
//加法题目生成采用答案倒推法,避免使用while过多消耗系统资源
int res = random (1, 100);
int add2 = random (1, res - 1); //保证add1 > 0
int add1 = res - add2;
printf ("%d + %d =", add1, add2);
outcome[0] = add1;
outcome[1] = '+';//相当于返回的ASCII码
outcome[2] = add2;
outcome[3] = res;
return outcome;
}
//减法
int *sub_exam () {
static int outcome[4];
int sum = random (1, 100);
int x = random (1, sum - 1); //保证res > 0
int res = sum - x;
printf ("%d - %d =", sum, x);
outcome[0] = sum;
outcome[1] = '-';//相当于返回的ASCII码
outcome[2] = x;
outcome[3] = res;
return outcome;
}
//乘法
int *mul_exam () {
static int outcome[4];
//下面同样利用了100/a <= 100.0/a的限制,避免了使用while
int res, a, b;
a = random (1, 51);
b = random (1, 100 / a);
res = a * b;
printf ("%d * %d =", a, b);
outcome[0] = a;
outcome[1] = '*';//相当于返回的ASCII码
outcome[2] = b;
outcome[3] = res;
return outcome;
}
//除法
int *div_exam () {
static int outcome[4];
//除法跟乘法生成方式一样,换个参数就行
int res, Divisor, Dividend;
res = random (1, 51);
Divisor = random (1, 100 / res);
Dividend = res * Divisor;
printf ("%d / %d =", Dividend, Divisor);
outcome[0] = Dividend;
outcome[1] = '/';//相当于返回的ASCII码
outcome[2] = Divisor;
outcome[3] = res;
return outcome;
}
//考试函数
void exam (char *user_id) {
int random_log[4];//已经出过的赋值1
int input;
int *p;
int i, tmp;
int mark = 0;//初始分数
int start_time = time (0);//初始时间
tmp = random (0, 3);//确保random_expect函数正确执行
for (i = 0; i < 10; i++) {
//用了个不太妙的方法
//对后3个题目进行操作,保证四中运算均出现
int flag=0;
if(i > 6){
int j;
for(j=0;j<4;j++){
if(!random_log[j]){
tmp = j;
flag = 1;
}
}
}
if(!flag){
tmp = random_except (0, 3, tmp);//保证每次题目不同
}
random_log[tmp] = 1;
p = exam_container[tmp] ();//调用出题函数获取数组
scanf ("%d", &input);
//判断答案是否正确
if (input == * (p + 3) ) {
mark += 10;
}
//赋值到exam_log,便于考试结束后输出
exam_log[i][0] = *p;
exam_log[i][1] = *++p;
exam_log[i][2] = *++p;
exam_log[i][3] = *++p;
exam_log[i][4] = input;
}
int end_time = time (0);
//写入数据
write_file (user_id, mark, end_time - start_time);
//输出考试详情
printf ("\n\n 问题 | 正确答案 | 你的答案");
for (i = 0; i < 10; i++) {
printf ("\n%3d%3c%3d=?|%10d|%d", exam_log[i][0], exam_log[i][1], exam_log[i][2], exam_log[i][3], exam_log[i][4]);
}
printf ("\n\n本次得分:%d\n时间:%d秒\n\n\n", mark, end_time - start_time);
}
//输出该ID的考试记录
int print_info (char *user_id) {
system("cls");
printf ("你以前的记录是:");
read_file();
int res = 0;
int i;
for (i = 0; i < count; i++) {
if (!strcmp (user_id, data[i][0]) ) {
printf ("\n%s %3s %s秒", data[i][0], data[i][1], data[i][2]);
res = 1;
}
}
return res;
}
C语言随机数生成:rand和srand函数
关于杨辉三角的一些思考
超市老板那道题
结语
保持学习,保持热爱!
评论 (1)