C语言:数组的增删查改2.0

引言

在之前的数组增删查改1.0项目中,我仅仅简单的封装了各函数,并通过"老土"且麻烦的方式,即通过每次注释代码,来调整想要实现的功能。该方法用户交互性差且不直观,而且不能实现功能的一起实现。因此,改进方法实现更高效,用户交互性更强的数组增删查改项目。

一、代码整体结构

本文实现的动态数组包含以下核心功能:

  • 初始化数组并赋予初始值
  • 向末尾添加元素
  • 按下标插入元素(支持自动扩容)
  • 按下标删除元素
  • 按值删除元素
  • 按下标 / 值查找元素
  • 修改指定元素
  • 交互式菜单操作

代码采用模块化设计,将不同功能封装为独立函数,主函数负责流程控制和用户交互。

二、代码逻辑及分析

1、头文件和宏定义

复制代码
#include <stdio.h>   // 提供输入输出函数(printf/scanf)
#include <stdlib.h>  // 提供动态内存分配函数(malloc/realloc/free)


#define len 100  // 初始容量的宏定义

功能说明

  • stdio.h 用于用户交互(输入输出提示信息)。
  • stdlib.h 是动态数组的核心依赖,提供内存分配(malloc)、扩容(realloc)和释放(free)函数。
  • len 定义数组初始容量,方便后续功能实现。

2. 添加元素功能

(1)向末尾添加元素

复制代码
// 添加单个元素到末尾
void add(int e, int arr[], int s) {
    arr[s] = e;
}
实现思路
  • 参数 e 为待添加元素,arr 为目标数组,s 为当前元素个数。
  • 直接通过 arr[s] = e 赋值。
用户界面:

(2)按下标插入元素(支持自动扩容)

复制代码
// 按照下标添加单个元素
void addByIndex(int *arr, int *size, int *capacity) {
    int index, newNum;
    printf("请输入要插入的下标:");
    scanf("%d", &index);
    printf("请输入要插入的元素:");
    scanf("%d", &newNum);

    // 检查下标合法性
    if (index < 0 || index > *size) {
        printf("下标不合法!\n");
        return;
    }

    // 检查是否需要扩容
    if (*size == *capacity - 1) {
        *capacity *= 2;  // 容量翻倍
        int *newarr = (int *)realloc(arr, *capacity * sizeof(int));
        if (newarr == NULL) {  // 检查内存分配是否成功
            printf("内存分配失败,扩容失败\n");
            return;
        }
        arr = newarr;  // 更新数组指针
        printf("数组扩容成功,新容量:%d\n", *capacity);
    }

    // 元素后移,为新元素腾出位置
    for (int i = *size; i > index; i--) {
        arr[i] = arr[i - 1];
    }
    arr[index] = newNum;  // 插入新元素
    (*size)++;            // 更新元素个数
}
实现思路
  1. 输入与合法性检查 :接收用户输入的下标和元素,检查下标是否在 [0, size] 范围内(size 位置即末尾插入)。
  2. 自动扩容 :当元素个数接近容量上限(size == capacity - 1)时,将容量翻倍,通过 realloc 重新分配内存,并更新数组指针。
  3. 元素后移:从最后一个元素开始,依次将元素向后移动 1 位,为新元素腾出位置。
  4. 插入与更新 :在目标下标插入新元素,元素总数 size 加 1。
关键要点
  • 扩容策略:容量翻倍(避免频繁扩容)。
用户界面:

3. 删除元素功能

(1)按下标删除元素

复制代码
// 根据下标删除元素
int removeElement(int index, int arr[], int s) {
    if (index < 0 || index >= s) {
        printf("输入的下标不在合理范围内\n");
        return 0;
    }
    int oldVal = arr[index];
    for (int i = index; i < s - 1; i++) {
        arr[i] = arr[i + 1];
    }
    arr[s - 1] = 0;
    return oldVal;
}
实现思路:
  1. 合法性检查 :确保下标在 [0, size-1] 范围内。
  2. 保存被删除值:记录目标下标元素,用于返回结果。
  3. 元素前移:从删除位置开始,用后一个元素覆盖当前元素,直到倒数第二个元素。
  4. 清空残留值:最后一个元素置 0。
用户界面:

(2)按值删除元素

复制代码
// 根据元素值删除
int deleteByElement(int arr[], int *size) {
    printf("请输入删除的元素值:");
    int deletedVal;
    scanf("%d", &deletedVal);

    int position = -1;
    for (int i = 0; i < *size; i++) {
        if (arr[i] == deletedVal) {
            position = i;
            break;
        }
    }

    if (position != -1) {
        int deleted = arr[position];
        for (int i = position; i < *size - 1; i++) {
            arr[i] = arr[i + 1];
        }
        (*size)--;
        printf("删除成功,被删除的元素是:%d\n", deleted);
        return deleted;
    } else {
        printf("未找到该元素,删除失败\n");
        return -1;
    }
}
实现思路:(仅删除第一个匹配项)
  1. 查找目标位置:遍历数组,找到第一个与目标值匹配的元素下标。
  2. 执行删除 :若找到,复用 "按下标删除" 的逻辑(元素前移),更新元素总数 size
  3. 结果反馈:返回被删除元素或提示未找到。
用户界面:

4. 查找与修改功能

(1)按值查找元素

复制代码
// 根据元素值查找
void searchElement(int arr[], int size) {
    printf("请输入查找的元素:");
    int find;
    scanf("%d", &find);

    int position = -1;
    for (int i = 0; i < size; i++) {
        if (arr[i] == find) {
            position = i;
            break;
        }
    }

    if (position != -1) {
        printf("已查找到元素%d,位于下标%d\n", find, position);
    } else {
        printf("未查找到元素%d\n", find);
    }
}
实现思路:遍历数组匹配目标值,记录第一个匹配项的下标并输出结果。
用户界面:

(2)修改元素

复制代码
// 修改元素
void reviseElement(int arr[], int size) {
    printf("请输入要修改的元素:");
    int rev;
    scanf("%d", &rev);

    int revpos = -1;
    for (int i = 0; i < size; i++) {
        if (arr[i] == rev) {
            revpos = i;
            break;
        }
    }

    if (revpos != -1) {
        printf("已找到元素%d,请输入修改后的元素:", rev);
        int newrev;
        scanf("%d", &newrev);
        arr[revpos] = newrev;
        printf("修改成功\n");
    } else {
        printf("未找到元素%d,无法修改\n", rev);
    }
}
实现思路:先查找目标值的下标,找到后直接通过下标赋值修改。
用户界面:

5. 主函数:初始化与交互逻辑

复制代码
int main() {
    int size = 8;           // 初始元素个数
    int capacity = len;      // 数组容量(动态管理,初始为len=100)
    int *arr = (int *)malloc(capacity * sizeof(int));  // 动态分配数组

    // 初始化数组内容
    int initArr[] = {1, 4, 5, 6, 9, 8, 2,3};
    for (int i = 0; i < size; i++) {
        arr[i] = initArr[i];
    }

    while (1) {
        printf("\n功能列表:\n");
        printf("1. 添加单个元素到末尾 -> 101\n");
        printf("2. 根据下标增加元素 -> 102\n");
        printf("3. 根据下标删除单个元素 -> 201\n");
        printf("4. 根据元素值删除 -> 202\n");
        printf("5. 根据下标查找单个元素 -> 301\n");
        printf("6. 根据元素值查找 -> 302\n");
        printf("7. 修改元素 -> 401\n");
        printf("8. 结束程序 -> 999\n");
        printf("请输入功能编号:");

        int id = 0;
        scanf("%d", &id);

        // 功能分支处理

	while (1) {
		printf("\n功能列表:\n");
		printf("1. 添加单个元素到末尾 -> 101\n");
		printf("2. 根据下标增加元素 -> 102\n");
		printf("3. 根据下标删除单个元素 -> 201\n");
		printf("4. 根据元素值删除 -> 202\n");
		printf("5. 根据下标查找单个元素 -> 301\n");
		printf("6. 根据元素值查找 -> 302\n");
		printf("7. 修改元素 -> 401\n");
		printf("8. 结束程序 -> 999\n");
		printf("请输入功能编号:");

		int id = 0;
		scanf("%d", &id);

		if (id == 101) {
			int num = 0;
			printf("请输入一个整数:");
			scanf("%d", &num);
			if (size < capacity) {  // 容量足够时直接添加到末尾
				add(num, arr, size);
				size++;
			} else {
				printf("数组已满,需扩容后添加(可使用"根据下标增加元素"功能)\n");
			}
		} else if (id == 102) {
			addByIndex(arr, &size, &capacity);  // 传递容量变量的地址
		} else if (id == 201) {
			int index = 0;
			printf("请输入要删除的下标:");
			scanf("%d", &index);
			int deleted = removeElement(index, arr, size);
			if (index >= 0 && index < size) {
				printf("被删除的元素是:%d\n", deleted);
				size--;
			}
		} else if (id == 202) {
			deleteByElement(arr, &size);
		} else if (id == 301) {
			int index;
			printf("请输入要查找的下标:");
			scanf("%d", &index);
			if (index >= 0 && index < size) {
				printf("下标%d对应的元素是:%d\n", index, arr[index]);
			} else {
				printf("下标不合法\n");
			}
		} else if (id == 302) {
			searchElement(arr, size);
		} else if (id == 401) {
			reviseElement(arr, size);
		} else if (id == 999) {
			free(arr);  // 释放动态内存
			exit(0);    // 正常退出程序
		} else {
			printf("输入不合法,请重新输入!\n");
		}


        // 打印当前数组状态
        printf("现有的元素:");
        for (int i = 0; i < size; i++) {
            printf("%d ", arr[i]);
        }
        printf("\n当前元素总数:%d,数组容量:%d\n", size, capacity);
    }

    free(arr);
    return 0;
}

实现思路:

  1. 初始化 :通过 malloc 动态分配初始内存(容量为 len=100),用预设数组 initArr 初始化前 8个元素。
  2. 交互循环 :通过 while(1) 实现死循环菜单,用户输入功能编号后执行对应函数,操作后即时打印数组状态(元素、个数、容量)。
  3. 内存释放 :程序退出前用 free(arr)释放动态内存,避免内存泄漏。

关键要点:

  • 初始状态:size=8(元素个数)与 capacity=100(容量)分离,体现动态数组 "逻辑长度" 与 "实际长度" 的区别。
  • 边界控制:所有操作均检查下标或容量合法性,避免越界访问。

用户界面:

三、核心设计思路总结

  1. 动态内存管理 :通过 malloc 分配初始内存,realloc 实现扩容,free 释放内存,解决静态数组容量固定的问题。
  2. 容量与大小分离capacity(实际容量)size(逻辑元素个数)独立管理,扩容仅改变 capacity,不影响元素数据。
  3. 元素移动策略:插入时 "后移" 腾位置,删除时 "前移" 覆盖,方向相反但均避免数据覆盖。
  4. 用户交互友好:通过菜单循环和即时状态打印,提升操作直观性。

四、项目受阻:

关键错误分析:

如上,在写代码过程中,编译遇到如图报错,一开始以为是define格式出现问题,后经过查资料,发现定义并未出错。后再仔细检查搜索反思,发现关键错误原因在于#define len 100 是宏定义(预处理时会被替换为常量 100),而 addByIndex(arr, &size, &len); 中传递 &len 是非法的 ------ 常量没有 "地址",编译器会报 "lvalue required as unary '&' operand" (需要左值作为 & 操作数)。

解决方式:

  1. 定义变量**capacity** 管理数组容量,初始值为宏 len(即 100)。
  2. 修改 addByIndex 的参数,将 int *len 改为 int *capacity,传递 capacity 的地址以支持动态扩容。
  3. 调整数组分配、扩容、容量判断的逻辑,统一使用 capacity 变量。
  4. 同时,让capacity(实际容量)size(逻辑元素个数)独立管理,扩容仅改变 capacity,不影响元素数据,实现容量与大小分离

五、注意事项

  1. 内存泄漏风险 :动态分配的内存必须用 free 释放,否则会导致内存泄漏。
  2. 扩容指针同步addByIndexarr = newarr 仅更新函数内指针,若 realloc 返回新地址,主函数指针可能未同步(AI分析指导),AI提到可通过二级指针优化。
  3. 边界检查 :所有涉及下标的操作必须验证范围,避免越界访问(如 removeElement 中的 index >= s 检查)。
相关推荐
漫漫不慢.6 小时前
算法练习-二分查找
java·开发语言·算法
R-G-B7 小时前
【18】C实战篇——C语言 文件读写【fputc、fgetc、fputs、fgets】
c语言·c语言文件读写·fputc·fgetc·fputs·fgets
掘根7 小时前
【Qt】绘图
开发语言·qt
咖啡续命又一天7 小时前
python 自动化采集 ChromeDriver 安装
开发语言·python·自动化
huohaiyu7 小时前
synchronized (Java)
java·开发语言·安全·synchronized
_OP_CHEN8 小时前
C++基础:(九)string类的使用与模拟实现
开发语言·c++·stl·string·string类·c++容器·stl模拟实现
蓝天智能8 小时前
QT MVC中View的特点及使用注意事项
开发语言·qt·mvc
木觞清8 小时前
喜马拉雅音频链接逆向实战
开发语言·前端·javascript
wuxuanok8 小时前
苍穹外卖 —— 公共字段填充
java·开发语言·spring boot·spring·mybatis