数字仓库 | C指针实战
前提概要
小智是数字世界的管理员,负责帮居民把数字存进仓库,还要帮忙查找、删除、替换数字。可刚上任就撞上第一个难题——仓库容量不好定,谁也不知道到底要存多少数字。容量定小了,存不下;定大了,又白白浪费空间。
幸好,小智之前就已经学习过这几篇教程,心里有了底,让我们开始吧!
功能设计
#include <stdio.h>
#include <stdlib.h>
int upSize(int size) {
if (size == 0) {
size++;
}
return size;
}
int main() {
// 使用数组存储数据
system("chcp 65001");
int size = 0; // 已存入的数据总数 作为下次存储数字的下标
int length = 4; // 当前仓库的总容量
int size0;
int *values = (int *)malloc(length * sizeof(int));
while (1) {
printf("=== 小智的数字仓库 ===\n");
printf("=== 201: 存入单个数字 \n");
printf("=== 202: 存入多个数字 \n");
printf("=== 203: 根据位置查找数字 \n");
printf("=== 204: 根据位置修改数字 \n");
printf("=== 205: 根据位置删除数字 \n");
printf("=== 206: 查看仓库中所有数字 \n");
printf("=== 207: 删除指定的所有相同数字 \n");
printf("=== 208: 对仓库中的所有数字排序 \n");
printf("=== 209: 退出仓库 \n");
printf("请输入功能编号: \n");
size0=upSize(size);
int cid = 0;
scanf("%d", &cid);
if (cid == 201) {
printf("请输入一个数字:\n");
int num = 0;
scanf("%d", &num);
values = addData(&size,&length,values,num);
}
else if (cid == 202) {
printf("请输入要存入数字总数:\n");
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
printf("请输入第%d个数字:\n",i+1);
int num = 0;
scanf("%d", &num);
values = addData(&size,&length,values,num);
}
}
else if (cid == 203) {
findData(size0,values);
}
else if (cid == 204) {
modifyData(size0,values);
}
else if (cid == 205) {
removeData(size0,&size,values);
}
else if (cid == 206) {
displayData(size,values);
}
else if (cid == 207) {
removeAll(&size,values);
}
else if (cid == 208) {
printf("输入1正序(从小到大),输入2倒序(从大到小),其它数字取消操作:\n");
int n;
scanf("%d", &n);
if (n == 1) {
sortData1(size,values);
}
else if (n == 2) {
sortData2(size,values);
}
else if (cid == 209) {
printf("感谢使用小智数字仓库");
break;
}
}
}
}
先来认识几个出场的关键变量吧:
size是访问下标,指向最后一个已存数据的位置,增删数据时它会跟着变化。size0是输入上限,超出这个值的输入统统无效。length是数组的总容量,和size长得有点像,但职责完全不同,可别混淆哦。values就是我们的数字仓库本体啦。
所有设计好的功能,都能在菜单里一眼看到并直接选择。
另外,这个程序采用函数封装的方式来组织功能,这样能让主函数保持简洁、可读性更强。
添加与扩容
int *addData(int *size,int *length,int values[],int num){
//先判断是否是存满了
if (*size == *length) {
// 需要扩容
int newLen = *length * 2;
int *newValues = (int *)malloc(newLen * sizeof(int)); // 新数组
// 迁移数据
for (int i = 0; i < *length; i++) {
newValues[i] = values[i];
}
// 更新数据
*length = newLen;
// 更新旧数组名存储的地址
values = newValues;
printf("扩容完成,当前的总容量升级为:%d\n", *length);
}
values[*size] = num;
*size = *size + 1;
return values;
}
「扩容」并不是独立的功能,而是藏在添加数据流程里的一个判断分支:只有当仓库真的满员了、size 追上了 length,才会临时触发扩容。
整个流程可以拆成两步:
- 添加数据:把
num放到values[*size]这个位置,存完之后让*size加 1,相当于把「已存数量」往后推一格。 - 扩容:如果存之前发现仓库已经满了,就先申请一块容量翻倍的新仓库(
length * 2),把旧仓库里的数字全部搬过去,再更新length,并让values指向新仓库,最后继续执行第 1 步完成存储。
简单说就是:先看看还有没有位置,没位置就换个大仓库,再把数字稳稳放进去。
打印全部
void displayData(int size,int values[]) {
printf("小智数字仓库当前数据:[");
for (int i = 0; i < size; i++) {
printf("%d,", values[i]);
}
printf("]\n");
}
这里有个小细节:遍历时用的是 i < size,而不是 i < length。因为 size 才代表仓库里真正存了多少数字,而 length 表示仓库的总容量。前面扩容判断里之所以写 *size == *length,是因为只有在仓库刚好满员的那一刻,两者才相等;正常情况下,size 是小于 length 的。
输入判定
用户输入什么,可不能全盘相信,所以这里得多留个心眼,加一道「输入判定」。推荐使用 do-while 结构,这样就算用户输错了也无妨,循环会贴心地再问一遍,直到输入合规为止,完全不会中途罢工退出。
int checkIndex(int size0) {
int index;
do {
printf("请输入一个编号(0~%d):\n",size0-1);
int index;
scanf("%d", &index);
if (index < 0 || index > size0-1) {
printf("输入的编号不存在,请重新输入!\n");
}
else {
return index;
}
} while (1);
}
删查改
查找和修改算是比较轻松的任务:直接顺着下标找过去,找到位置就能读或改。删除就稍微有点讲究了,先别急着往下翻,你可以试着看看下面的程序,能不能自己看出点门道。
void removeData(int size0,int *size,int *values) {
int n=checkIndex(size0),oldNum;
oldNum = values[n];
for (int i = n; i <= *size-n; i++) {
values[i] = values[i+1];
}
*size = *size - 1;
printf("数字仓库已删除%d号位储存的数字(%d):\n",n,oldNum);
}
void findData(int size0,int values[]) {
int n=checkIndex(size0);
printf("数字仓库的%d号位储存的数字是:%d\n",n,values[n]);
}
void modifyData(int size0,int *values) {
int num,oldNum,n=checkIndex(size0);
printf("请输入一个新数字(原位置的数字是%d):\n",values[n]);
scanf("%d", &num);
oldNum = values[n];
values[n] = num;
printf("数字仓库的%d号位储存的数字由%d改为%d:\n",n,oldNum,values[n]);
}
可以注意到,删除的巧妙之处在于:不能留一个空位在那里,得把被删除数字后面的每个数字依次向前挪一格。程序里用的正是这种「覆盖」方法,前一个位置被后一个数字顶上去,仓库就重新变得整整齐齐。
细心的你可能已经发现啦:删除、查找、修改这三个功能,都整齐地加上了刚才的输入判定。无论是查找、修改还是删除,只要位置不合规,都会先被 do-while 温柔地拦下,直到拿到合法编号为止。
排序功能
排序这里选用的是最经典的冒泡排序:相邻两个数字两两比较,谁更大谁就往后挪,像气泡一样一点一点浮到正确的位置。sortData1 负责从小到大正序排列,sortData2 则是它的“镜像版”,把大小关系一翻转,立刻变成从大到小的倒序。至于选择排序、插入排序这些其他路子,代码也完全可以换成它们,本文就先不展开啦。
void sortData1(int size,int *values) {
int d;
for (int j = 0; j < size-1; j++) {
for (int i = 0; i < size-1; i++) {
if (values[i] > values[i+1]) {
d = values[i];
values[i] = values[i+1];
values[i+1] = d;
}
}
}
}
void sortData2(int size,int *values) {
int d;
for (int j = 0; j < size-1; j++) {
for (int i = 0; i < size-1; i++) {
if (values[i] < values[i+1]) {
d = values[i];
values[i] = values[i+1];
values[i+1] = d;
}
}
}
}
删掉全部指定数字
这个功能有点像仓库里的一次“大扫除”:用户随便输入一个数字,程序就要把仓库里所有跟它一模一样的数字统统清理掉,一个都不留。你可以别着急看答案,自己先想想。
void removeAll(int *size,int *values) {
int num,move=0;
printf("请输入一个数字:\n");
scanf("%d", &num);
for (int i = 0; i < *size; i++) {
if (values[i] == num) {
move++;
}
else {
values[i-move] = values[i];
}
}
*size=*size-move;
printf("数字仓库已删除所有的%d\n",num);
}
本程序采用的方法是边扫描边覆盖:move 就像一个小账本,负责记录已经碰到了多少个要删除的数字。遍历时,遇到要删除的数字就让 move 加 1;遇到要保留的数字,就把它整体“前移” move 格,刚好补上前面空出来的位置。一轮走完后,所有不要的数字都被要保留的数字顶掉了,最后把 size 减去 move,仓库就完成了瘦身,干净又整齐。
相比反复调用“删一个数字”再搬来搬去,这里只从头到尾扫了一遍,思路更轻巧高效。
完整源码
恭喜你完成了“数字仓库”项目!
#include <stdio.h>
#include <stdlib.h>
int upSize(int size) {
if (size == 0) {
size++;
}
return size;
}
int checkIndex(int size0) {
int index;
do {
printf("请输入一个编号(0~%d):\n",size0-1);
int index;
scanf("%d", &index);
if (index < 0 || index > size0-1) {
printf("输入的编号不存在,请重新输入!\n");
}
else {
return index;
}
} while (1);
}
int *addData(int *size,int *length,int values[],int num){
//先判断是否是存满了
if (*size == *length) {
// 需要扩容
int newLen = *length * 2;
int *newValues = (int *)malloc(newLen * sizeof(int)); // 新数组
// 迁移数据
for (int i = 0; i < *length; i++) {
newValues[i] = values[i];
}
// 更新数据
*length = newLen;
// 更新旧数组名存储的地址
values = newValues;
printf("扩容完成,当前的总容量升级为:%d\n", *length);
}
values[*size] = num;
*size = *size + 1;
return values;
}
void findData(int size0,int values[]) {
int n=checkIndex(size0);
printf("数字仓库的%d号位储存的数字是:%d\n",n,values[n]);
}
void modifyData(int size0,int *values) {
int num,oldNum,n=checkIndex(size0);
printf("请输入一个新数字(原位置的数字是%d):\n",values[n]);
scanf("%d", &num);
oldNum = values[n];
values[n] = num;
printf("数字仓库的%d号位储存的数字由%d改为%d:\n",n,oldNum,values[n]);
}
void removeData(int size0,int *size,int *values) {
int n=checkIndex(size0),oldNum;
oldNum = values[n];
for (int i = n; i <= *size-n; i++) {
values[i] = values[i+1];
}
*size = *size - 1;
printf("数字仓库已删除%d号位储存的数字(%d):\n",n,oldNum);
}
void removeAll(int *size,int *values) {
int num,move=0;
printf("请输入一个数字:\n");
scanf("%d", &num);
for (int i = 0; i < *size; i++) {
if (values[i] == num) {
move++;
}
else {
values[i-move] = values[i];
}
}
*size=*size-move;
printf("数字仓库已删除所有的%d\n",num);
}
void sortData1(int size,int *values) {
int d;
for (int j = 0; j < size-1; j++) {
for (int i = 0; i < size-1; i++) {
if (values[i] > values[i+1]) {
d = values[i];
values[i] = values[i+1];
values[i+1] = d;
}
}
}
}
void sortData2(int size,int *values) {
int d;
for (int j = 0; j < size-1; j++) {
for (int i = 0; i < size-1; i++) {
if (values[i] < values[i+1]) {
d = values[i];
values[i] = values[i+1];
values[i+1] = d;
}
}
}
}
void displayData(int size,int values[]) {
printf("小智数字仓库当前数据:[");
for (int i = 0; i < size; i++) {
printf("%d,", values[i]);
}
printf("]\n");
}
int main() {
// 使用数组存储数据
system("chcp 65001");
int size = 0; // 已存入的数据总数 作为下次存储数字的下标
int length = 4; // 当前仓库的总容量
int size0;
int *values = (int *)malloc(length * sizeof(int));
while (1) {
printf("=== 小智的数字仓库 ===\n");
printf("=== 201: 存入单个数字 \n");
printf("=== 202: 存入多个数字 \n");
printf("=== 203: 根据位置查找数字 \n");
printf("=== 204: 根据位置修改数字 \n");
printf("=== 205: 根据位置删除数字 \n");
printf("=== 206: 查看仓库中所有数字 \n");
printf("=== 207: 删除指定的所有相同数字 \n");
printf("=== 208: 对仓库中的所有数字排序 \n");
printf("=== 209: 退出仓库 \n");
printf("请输入功能编号: \n");
size0=upSize(size);
int cid = 0;
scanf("%d", &cid);
if (cid == 201) {
printf("请输入一个数字:\n");
int num = 0;
scanf("%d", &num);
values = addData(&size,&length,values,num);
}
else if (cid == 202) {
printf("请输入要存入数字总数:\n");
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
printf("请输入第%d个数字:\n",i+1);
int num = 0;
scanf("%d", &num);
values = addData(&size,&length,values,num);
}
}
else if (cid == 203) {
findData(size0,values);
}
else if (cid == 204) {
modifyData(size0,values);
}
else if (cid == 205) {
removeData(size0,&size,values);
}
else if (cid == 206) {
displayData(size,values);
}
else if (cid == 207) {
removeAll(&size,values);
}
else if (cid == 208) {
printf("输入1正序(从小到大),输入2倒序(从大到小),其它数字取消操作:\n");
int n;
scanf("%d", &n);
if (n == 1) {
sortData1(size,values);
}
else if (n == 2) {
sortData2(size,values);
}
else if (cid == 209) {
printf("感谢使用小智数字仓库");
break;
}
}
}
}
更多推荐




所有评论(0)