前言
简单学习 C 语言,制作一个商店页面。主要涉及的内容有:
- 文件读取
- 输入输出
- 指针
- 内存申请、使用、释放
- 流程控制:循环、判断、选择
- 函数定义
- 结构体定义
- 变量类型转换
- 编码设置(非必须)
代码
简单代码一览:
#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '\0'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '\0'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '\0'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '\0'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '\0'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '\0'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '\0'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '\0'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '#include <stdio.h> #include <stdlib.h> #include <windows.h> typedef struct { char *name; double prize; char *describe; } Shop; int shop_count = 0; // 记录第几个商品了 Shop **shop = NULL; // 给商品分配内存 double money = 100; // 目前的钱 int *cart = NULL; // 购物车 int cart_count = 0; // 购物车商品数量 void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif } char *file_getline(FILE *file) { // 逐一读字符 int c; long size = 1; char *line = malloc(1); line[0] = '\0'; while ((c = fgetc(file)) != EOF && c != '\n') { char *new_line = realloc(line, ++size); // realloc 重新分配内存,并会释放原来的内存 if (new_line == NULL) exit(-1); // 如果不成功原来的内存不会释放 new_line[size - 2] = c; new_line[size - 1] = '\0'; line = new_line; } return line; } void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); } void close_shop() { // 释放内存 for (int i = 0; i < shop_count; i++) { free(shop[i]); } // 释放商品列表内存 free(shop); } void print_shop() { puts("我们有如下商品:"); puts("*************************************************************"); for (int i = 0; i < shop_count; i++) { printf("商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, shop[i]->name, shop[i]->describe, shop[i]->prize); } puts("*************************************************************"); } /* * 获取用户输入,仍有问题,比如效率太慢。以后改用缓存区。 */ char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop'; user = new_user; } return user; } void print_help() { puts("* help -- 输出帮助文档"); puts("* goods -- 输出商品"); puts("* cart -- 查看我的钱包和购物车"); puts("* buy [编号] -- 购买商品"); puts("* remove [编号] -- 移除购物车商品"); puts("* bye -- 退出程序"); } int main(void) { // 设置一些本地化的东东 setup(); // 开启商店 start_shop(); puts("欢迎光临没什么超市,我们这里有许多好吃的东西哦。请问你要买点什么呢?(*^▽^*)"); print_shop(); puts("输入 help 显示帮助。"); char *user = NULL; while (1) { printf("> "); // 不用 scanf 我使用 getchar 自动管理内存 user = get_input(); if (strcmp(user, "goods") == 0) { print_shop(); } else if (strcmp(user, "help") == 0) { print_help(); } else if (strcmp(user, "bye") == 0) { break; } else if (strcmp(user, "cart") == 0) { printf("当前你有 %.2f 元。\n", money); if (cart_count == 0) { puts("你的购物车里目前没有东西。"); } else { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop->name, shop->describe, shop->prize); total_money += shop->prize; } printf("一共 %.2f 元。\n", total_money); } } else if (strcmp(user, "check") == 0) { puts("你的购物车里有:"); double total_money = 0; for (int i = 0; i < cart_count; i++) { int code = cart[i]; printf("(%d) 商品编号 %d : %s %s 售价:%.2f/份\n", i + 1, code + 1, shop->name, shop->describe, shop->prize); total_money += shop->prize; } printf("一共 %.2f 元。是否立刻结账?[Y/n]:", total_money); char c; if ((c = tolower(getchar())) != 'n') { if (c != '\n') getchar(); // 去掉换行符 if (money < total_money) { puts("你的钱不够!请充值!"); } else { money -= total_money; free(cart); cart = NULL; cart_count = 0; puts("购买成功,东西已经寄到你的赛博之家了。"); } } } else { // 处理多参数 const char *token = strtok(user, " "); if (token == NULL) continue; // 第一次都没有就是没输入了 if (strcmp(token, "buy") == 0) { token = strtok(NULL, " "); if (token == NULL) { puts("请输入产品编号。"); } else { int id = strtol(token, NULL, 10); if (id <= shop_count && id > 0) { // 重新分配 cart 的内存 cart_count++; int *new_cart = realloc(cart, sizeof(int) * cart_count); new_cart[cart_count - 1] = id - 1; // 第二参数转化失败的指针,最后一个参数为进制 cart = new_cart; printf("购买了 %s 。\n", shop[id - 1]->name); } else { puts("请输入正确的编号。"); } } } else if (strcmp(token, "remove") == 0) { token = strtok(NULL, " "); if (token == NULL) { puts("请输入产品编号。"); } else { int id = strtol(token, NULL, 10); if (id <= shop_count && id > 0 && cart_count > 0) { int pos = 0; // 先找到产品 for (int i = 0; i < cart_count; i++) { if (cart[i] + 1 == id) { pos = i; break; } } printf("删除了 %s 。\n", shop[id - 1]->name); // 移动数组 for (int i = pos; i < cart_count - 1; i++) { cart[pos] = cart[pos + 1]; } cart_count -= 1; int *new_cart = realloc(cart, sizeof(int) * cart_count); cart = new_cart; } else { puts("请输入正确的编号。"); } } } } free(user); } // 关闭商店 close_shop(); return 0; }成果概述
关于使用 utf-8 编码
由于现在使用的都是 utf-8 编码了,但是国内电脑都会默认将程序的编码页设置为 gbk 编码,以至于不能涵盖特殊符号。所以一开始就直接尝试将程序的代码页转化为 utf-8 。需要注意一定是并不是使用宽字符,据查过来的资料,宽字符默认使用的是 utf-16 并不是 utf-8 ,可能有显示不出来的问题。设置编码格式关键在于这个函数:
#include <windows.h> void setup() { // 设置控制台代码页为UTF-8 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入代码页为UTF-8 SetConsoleCP(CP_UTF8); #ifdef DEBUG setvbuf(stdout, NULL, _IONBF, 0); // 设置为无缓冲模式 #endif }其中的
SetConsole...
相当于在控制台(Windows 下)输入chcp 65001
,此外我还研究了在 Clion 中进行 utf-8 输入输出,发现最好的情况是开启 Clion 的模拟控制台,然后运行程序,将所有编码全部改为 utf-8 即可进行 utf-8 的输入输出。此外在使用 Clion 调试时可能会出现输出的内容没有及时显示的问题,是因为没有及时刷新输出缓存,使用setvbuf
设置输出为无缓存模式。获取用户输入
获取用户输入可以使用诸如
scanf
getchar
gets
,但是这些对输入都有长度要求,比如不能超过字符串数组长度。那有没有自动扩大长度的方法呢?我们可以使用realloc
来扩大内存。char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = 'char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; }'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = 'char *get_input() { char user_length = 1; // 用户输入长度 char *user = malloc(sizeof(char)); if (user == NULL) exit(-1); user[0] = '\0'; // 不用 scanf 我使用 getchar 自动管理内存 char c; while ((c = getchar()) != '\n') { user_length += 1; char *new_user = realloc(user, sizeof(char) * user_length); if (new_user == NULL) exit(-1); new_user[user_length - 2] = c; new_user[user_length - 1] = '\0'; user = new_user; } return user; }'; user = new_user; } return user; }需要注意字符串末尾一定是
\0
,这种方法还有一种不足之处是效率比较慢,没有设立缓存区。读取商品并逐步扩大
商品读取也使用了
realloc
来扩大内存,这样我们不需要预先设置商品的多少,直接让商品多少取决于文件行数,更便于添加商品。void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != 'void start_shop() { // 读取文件 FILE *file = fopen("shops.csv", "rb"); char *line; do { // 重新分配内存 Shop **new_shop = realloc(shop, sizeof(Shop *) * (shop_count + 1)); if (new_shop == NULL) exit(-1); shop = new_shop; // 分配一个商品的内存 shop[shop_count] = malloc(sizeof(Shop)); line = file_getline(file); const char *token = strtok(line, ","); int shop_c = 0; // 标记字段 while (token != NULL) { switch (shop_c) { case 0: shop[shop_count]->name = strdup(token); break; case 1: shop[shop_count]->prize = strtod(token, NULL); // 第二个参数是一个 char** 类型的指针,用于存储指向转换后第一个未处理字符 break; case 2: shop[shop_count]->describe = strdup(token); break; default: break; } shop_c += 1; token = strtok(NULL, ","); } shop_count += 1; } while (line[0] != '\0'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); }'); shop_count -= 1; // 所有商品读取完成,释放文件 fclose(file); }需要注意的是,
realloc
函数的第二参数的内存所需用量一定是根据变量对象的数据类型来的,比如**new_shop
的一个大小是sizeof (Shop *)
不是
sizeof (Shop) 。
字符串处理
程序中使用了多个字符串函数,如下:
- strtok -- 分割字符串
- strdup -- 复制字符串(完全复制,自动分配指针)
- strtol -- 字符串到整数
- strtod -- 字符串到浮点
- strcmp -- 字符串比较,相同返回 0 。
指针的使用
程序许多地方使用指针,一般的理解是
- 假设
char p
,p
就是一个字符对象。 - 假设
char *p
,p
就是 一个指向字符对象的指针,*p
就是这个字符对象。 - 假设
char **p
,p
就是存储字符对象的指针的指针,p[i]
就是字符对象的指针。
其中 char *p[]
近似等价于 char **p
,不同的是前者指的是数组,后者指的是指针,两种均可以使用 p[]
进行定位元素,前者是一个指针数组,指针数组,数组大小是固定的,不能更改,但是后者进行的是指针运算,p[1]
相当于 p + sizeof(char *)
,可以进行动态分配。