#C++ Day35 Basic Data Structure Chapter2  November 20 2025

#include <iostream>

using namespace std;

#define eleType int

struct SequentialList {//定义顺序表结构

eleType* elements;

int size; //顺序表当前的大小,元素的个数

int capacity; //顺序表的容量,就是数组的大小 

};

void intializeList(SequentialList* list, int capacity) {//初始化顺序表

list->elements = new eleType[capacity];

list->size = 0;

list->capacity = capacity;

}

void destroyList(SequentialList* list) {//删除整个顺序表

delete[] list->elements;

}

int main() {

return 0;

}

这是一条 C 或 C++ 语言中的预处理指令 (Preprocessor Directive)。

简单来说,它的作用是告诉编译器在编译代码之前,把程序中出现的所有 eleType 文本直接替换成 int。这样做的好处是灵活性:如果你在写一个数据结构(比如栈或队列),现在用它存整数 (int),但以后想改成存小数 (float),你只需要把这一行改成 #define eleType float,而不需要去修改代码里的每一个变量声明。

为了帮你彻底搞懂这个概念以及它的最佳实践,我制定了一个简单的学习计划:

学习计划概览:

 * 深入理解 #define 的机制(它是如何进行“复制粘贴”工作的?)

 * 为什么要给类型起别名(为什么不直接写 int?eleType 代表什么?)

 * 更好的替代方案(为什么现代程序员更喜欢用 typedef 或 using?)

这不仅能帮你读懂这行代码,还能让你写出更专业的 C/C++ 代码。我们先从第一步:#define 到底是怎么工作的开始,好吗?

太棒了!让我们开始第一步。

1. 深入理解 #define:它只是个“搜索替换”工具

要理解 #define,你得先认识 C/C++ 编译过程中的一位幕后英雄——预处理器 (Preprocessor)。

在编译器(Compiler)真正开始把你的代码翻译成机器语言之前,预处理器会先扫一遍你的代码。当它看到以 # 开头的指令(比如 #define 或 #include)时,它会立刻执行操作。

对于 #define A B,它的逻辑非常简单粗暴:在源码里找到所有的 “A”,把它们全部替换成 “B”。

这就好比你在 Word 文档里用了“查找和替换”功能。预处理器完全不懂 C 语言的语法(它不知道什么是变量、什么是函数),它只管文本替换。

👀 视觉演示:替换前后

想象一下,这是你写的代码(源文件):

// 你的代码 (Source Code)

#define eleType int

void main() {

    eleType a = 10;

    eleType b = 20;

}

在预处理器工作完之后,编译器真正看到的“干净”代码其实是这样的(预处理后):

// 编译器眼中的代码

// 注意:#define 那一行已经消失了

void main() {

    int a = 10;   // eleType 被替换成了 int

    int b = 20;   // eleType 被替换成了 int

}

有趣的小知识: 就像上面说的,预处理器其实有点“傻”。如果你写 #define TRUE FALSE,它真的会乖乖地把代码里所有的 TRUE 都改成 FALSE,进而导致你的程序逻辑完全颠倒,而且编译器还不会报错!这就是所谓的“宏定义陷阱”。

🧠 互动小测验

为了确认你掌握了这个“简单替换”的概念,请看下面这段代码。

假设我们有这行指令:

#define PI 3.14

下面是你的代码:

float area = PI * r * r;

print(“The value of PI is important.”);

请问:预处理器处理完之后,第二行代码里的字符串 “The value of PI is important.” 会变吗?为什么?

回答得非常棒!💯

完全正确。预处理器虽然是在做“查找替换”,但它也遵守基本的规则:它不会去动双引号 “” 里面的内容。所以字符串里的 PI 还是 PI,而代码逻辑里的 PI 会变成 3.14。你已经抓住了预处理的核心逻辑!

1.2 宏定义的常见用途:不仅仅是定义类型

虽然你最初问的是 #define eleType int(定义类型),但在 C 语言的世界里,#define 其实更多时候是被用来定义常量(Constants)。

试想一下,你在写一个程序处理学生的成绩,全校这学期最多招收 100 个学生。你在代码里到处写 100:

int scores[100];

for(int i=0; i<100; i++) { … }

if (student_id > 100) { … }

这种直接写在代码里的数字,程序员给它取了个很形象的名字,叫**“魔术数字” (Magic Numbers)** —— 就像魔术一样突然冒出来的数字,别人读代码时会一脸懵:“为什么是 100?不是 99?这代表什么?”

如果你用 #define,代码就会清晰很多:

#define MAX_STUDENTS 100  // 一目了然

int scores[MAX_STUDENTS];

for(int i=0; i<MAX_STUDENTS; i++) { … }

这样做有两个巨大的好处:

 * 可读性:看到 MAX_STUDENTS 就知道意思是“最大学生数”。

 * 易修改:明年扩招变成 200 人了,你只需要改一行代码,不用满世界去找那个 100。

🕵️‍♂️ 侦探小练习

我们要结束第一步了,来个小测试巩固一下。

除了定义“类型”(像 eleType)和“常量数值”(像 100),#define 其实还可以定义“看起来像函数的东西”(虽然我们还没细讲,但你可以猜猜看)。

请看下面这个宏定义:

#define SQUARE(x) x * x

如果我在代码里写 SQUARE(5),预处理器会把它变成什么?

A. 25 (直接算出结果)

B. 5 * 5 (进行文本替换)

C. int square(int x) { return x * x; } (生成一个函数)

完全正确!答案就是 B。👏

这就是关键所在:预处理器只是个“搬运工”,它不负责计算,只负责把 x 换成 5,所以代码变成了 5 * 5。虽然结果和 A 一样,但过程截然不同。(这也是它的一个隐患,比如传入 1+1 时,会变成 1+1 * 1+1 = 3,而不是我们期望的 4,不过这是进阶话题了,你只要记得它是纯文本替换就好)。

2. 为什么要给类型起别名?(Type Aliasing)

现在回到你最初的代码:#define eleType int。

你可能会问:“直接写 int 不香吗?为什么要多此一举造个 eleType?”

2.1 解密命名:eleType 是什么?

通常,当你看到 eleType,它是 Element Type(元素类型)的缩写。

这行代码极大概率出现在数据结构(比如栈 Stack、队列 Queue、链表 List)的实现代码中。

想象一下,你要写一个栈 (Stack)。

如果你的栈是用来存整数的,你可能会这样写结构体:

struct Stack {

    int data[100];  // 数组存 int

    int top;

};

void push(struct Stack* s, int value); // 压入 int

int pop(struct Stack* s);              // 弹出 int

这没问题。但是,如果下周你的老师或者老板说:“嘿,我们现在需要这个栈来存小数 (float),或者字符 (char)。”

如果你直接写死了 int,那你就要去修改结构体里的定义、修改 push 函数的参数、修改 pop 函数的返回值……你需要改好几个地方,甚至几十个地方。漏改一个,程序就报错。

🌟 这里的魔法在于“抽象”

如果我们用 eleType 作为中间人:

#define eleType int  // <— 控制中心

struct Stack {

    eleType data[100];  // 这里的类型听指挥

    int top;

};

void push(struct Stack* s, eleType value); // 这里的类型听指挥

eleType pop(struct Stack* s);              // 这里的类型听指挥

现在,如果要从存整数改成存小数,你只需要做一件事:

把第一行改成 #define eleType float。

砰! 整个栈的代码瞬间变成了存储 float 的栈。

这就是可维护性 (Maintainability)。

🔄 角色扮演小任务

为了让你体验这种“一键切换”的快感,我们来模拟一下。

假设你是一个游戏开发者。你正在写一个背包系统 Bag,用来存放道具 ID。目前道具 ID 是简单的数字(int)。

代码大概是这样:

#define ItemID int 

struct Bag {

    ItemID items[50];

};

突然,策划跑来跟你说:“不行不行,道具 ID 不够用了,我们要换成超大数字(long long)才能存得下!”

请问: 为了满足策划的需求,你需要对上面的代码做哪些具体的修改?

干得漂亮!🎉

你看,你只修改了这一行代码,就完成了整个游戏背包系统的升级。这就是程序员追求的“高内聚,低耦合”——改动越少,出 bug 的概率就越低。

除此之外,这还有助于可移植性 (Portability)。比如有些微型电脑(单片机)的 int 只有 16 位,而你现在的电脑 int 是 32 位。如果使用了类型别名,当你把代码移植到新机器时,只需要修改那个 #define 就能适配硬件了。

3. 更好的替代方案:typedef 和 using

虽然 #define 很有用,但在定义类型这件事上,它其实有点“粗暴”。因为它只是简单的文本替换,有时候会带来意想不到的问题,而且编译器在报错时可能只会显示 int 而不是你的别名 eleType,让调试变难。

所以,在 C 和 C++ 中,我们更推荐使用专门的工具。

3.1 C 语言的老牌工具:typedef

typedef 是 “Type Definition” 的缩写。它的作用是给现有的类型起一个“外号”。

语法对比:

| 方式 | 代码 | 谁在处理? | 特点 |

|—|—|—|—|

| 宏定义 | #define eleType int | 预处理器 | 简单替换,不需要分号 ; |

| 类型定义 | typedef int eleType; | 编译器 | 真正的类型别名,需要分号 ; |

注意语法的区别:

 * #define 是:旧名字 新名字 (中间没分号)

 * typedef 是:typedef 旧名字 新名字; (像定义变量一样)

例子:

typedef int eleType; // 告诉编译器:eleType 就是 int 的别名

struct Stack {

    eleType data[100]; // 用法完全一样

};

这样做的好处是,编译器明确知道 eleType 是一个类型,它能帮你做更好的类型检查。

3.2 现代 C++ 的新宠:using (强烈推荐)

如果你是在写 C++ (尤其是 C++11 标准之后),有一个更直观、更现代的写法,那就是 using 关键字。

它的可读性最强,读起来就像赋值一样:

using eleType = int;  // 意思是:让 eleType 等于 int

这和 typedef int eleType; 的功能完全一样,但写起来更符合人类直觉(名字在左边,类型在右边)。

📝 巩固练习:代码“翻译”

为了确保你掌握了这三种方式,我们来做一个简单的“翻译”练习。

请把下面这行用 #define 写的代码,分别“翻译”成 typedef 写法和 C++ 的 using 写法。

原始代码:

#define AgeType float

请写出:

 * typedef 写法: __________

 * using 写法: __________

逻辑完全正确!不管是 typedef 的顺序还是 using 的赋值写法,你都搞对了。👏

不过,有一个非常重要的小细节需要注意:分号 ;。

 * #define 是预处理指令,它不需要分号(如果有分号,分号也会被一起替换进代码里,导致出错)。

 * typedef 和 using 是 C/C++ 语句,它们必须以分号结尾。

所以完美的写法是:

 * typedef float AgeType;

 * using AgeType = float;

记住这个区别,你的代码就能一次编译通过了!

🎓 总结:这一行代码背后的智慧

我们要完成这次学习了。回到你最初的问题 define eleType int,我们学到了:

 * 它是谁:预处理指令,负责在编译前把 eleType 全部文本替换成 int。

 * 为什么用它:为了灵活性和可维护性。如果你想把存整数的栈改成存小数的栈,只需要改这一行,不用改几百行代码。

 * 谁更好:虽然 #define 能用,但在现代 C++ 编程中,我们更推荐使用 typedef 或 using,因为它们更安全,编译器更懂它们。

你现在对这个概念应该非常有信心了吧?

要不要来一个只有 3 道题的终极小测验,看看你是否已经完全掌握了这个知识点?

太棒了!那我们就开始吧,这是第一题。

问题 1:预处理器的陷阱

假设我们在代码里写了这样一个宏定义:

#define ADD(x) x + x

然后我们在主函数里计算这个表达式:

int result = ADD(5) * 2;

请问,result 的值最后是多少?

(提示:请把自己当成只做“复制粘贴”的预处理器,把 ADD(5) 替换进后面的式子里,然后再算数学题。)

A. 20

B. 15

C. 10

你做得非常棒!答案确实是 B. 15。

 * 预处理器替换:ADD(5) * 2 被替换成了 5 + 5 * 2。

 * 运算顺序:乘法 * 的优先级高于加法 +。所以是先算 5 \times 2 = 10。

 * 最终结果: 5 + 10 = 15。

你不仅理解了 #define 是文本替换,还正确运用了数学运算的优先级规则。太厉害了!这也是为什么在写宏的时候,我们总是建议用括号把所有参数和整个宏体都括起来,以避免这种“陷阱”:#define ADD(x) ( (x) + (x) )。

📝 问题 2:语法大不同

接下来我们测试一下 #define 和其他替代方案的语法差异,这是我们刚刚纠正过的重点。

在 C/C++ 代码中,下面哪一行类型别名定义的写法是不正确的(即会造成编译错误)?

A. typedef float PriceType;

B. using CountType = int;

C. typedef int IDType

太棒了!你的答案 C 完全正确!🎯

typedef int IDType 缺少了语句末尾的分号,所以它会导致编译错误。你很清楚地掌握了 #define 和 typedef/using 在语法上的最大区别:前者不带分号,后者需要分号。

📝 问题 3:最终考验:应用价值

这是我们的最后一题,它将检验你对类型别名核心价值的理解。

假设你的代码库中有 50 处使用了 typedef int IDType;。如果有一天,由于业务需求,你需要把 ID 类型从 int 换成 long long。你只需要修改一行代码即可完成切换。

这种使用类型别名(而不是直接写 int)的方式,主要体现了代码的哪种优势?

A. 可读性 (Readability):让代码看起来更漂亮。

B. 可移植性 (Portability):方便代码在不同操作系统间运行。

C. 可维护性 (Maintainability):方便在未来对代码进行大规模修改。

你完全正确!答案是 C. 可维护性 (Maintainability)。👍

这个场景的重点在于“大规模修改”:只改一行代码就能完成对整个系统的类型升级。这种简化未来维护工作的特性就是类型别名的最大价值。

当然,A (可读性) 和 B (可移植性) 也是类型别名的优点,但它们是次要的:

 * 可读性:IDType 确实比 int 更清楚。

 * 可移植性:在不同系统上修改 #define 确实能帮助移植。

 * 可维护性:能够快速、准确、低风险地适应需求变化,这才是它在大型项目中不可替代的核心优势。

🚀 学习回顾总结

恭喜你!你已经完全掌握了 #define eleType int 这行代码背后的所有概念、用途和替代方案。

学习成果总结:

| 学习要点 | 你的掌握情况 |

|—|—|

| #define 机制 | 完美理解纯文本替换,并成功解决了宏定义的陷阱问题(Q1)。 |

| 别名核心价值 | 明确了类型别名是为了提高代码的可维护性(Q3)。 |

| 高级语法 | 掌握了 typedef 和 using 的语法,并能分辨它们与 #define 的关键区别(如分号的使用,Q2)。 |

你在整个学习过程中的表现非常出色!三次测验都回答正确,并且在学习活动中也展示了对 #define 和类型别名概念的清晰理解。

你现在不仅知道 #define eleType int 是什么,还知道为什么在现代编程中,我们更喜欢 using eleType = int;。

你觉得我们今天是否成功帮助你达成了学习目标呢?我随时可以帮助你学习更多关于 C/C++、数据结构或其他学术话题!

太棒了!听到你这么说我感到非常开心!😃

很高兴能作为你的学习教练,帮助你掌握 C/C++ 中类型别名的核心知识。你今天的学习效率很高,对概念的理解非常透彻!

如果你未来在编程、数学、科学或其他学术领域有任何新的学习目标或遇到难题,随时都可以来找我。祝你学习顺利,编码愉快!

这行代码的核心作用是 在索引非法时,主动抛出「非法参数异常」,将错误信息传递给调用者,属于 C++ 中「异常处理机制」的关键用法。下面从「是什么、为什么用、怎么用规范、如何优化」四个维度详细拆解:

一、核心解读:这行代码到底做了什么?

cpp

运行

throw std::invalid_argument(“Invalid index”);

  • throw 关键字:中断当前函数执行,将后续的错误处理逻辑 “移交” 给调用者(必须在调用处用 try-catch 捕获,否则程序直接崩溃)。
  • std::invalid_argument:C++ 标准库提供的「异常类」(定义在 <stdexcept> 头文件中),专门用于表示「函数参数不合法」的场景(比如传入的索引、数值不符合函数要求)。
  • “Invalid index”:异常的「错误信息字符串」,用于描述具体错误原因,方便调用者定位问题。

二、为什么要用异常,而不是返回错误码?

在顺序表的 insert 函数中,索引非法是 “非常规错误”(正常调用时应避免),用异常比返回错误码更合适:

  • 错误处理与业务逻辑分离:无需在调用处每次都判断返回值(比如 if (insert(…) == -1) { 处理错误 }),代码更简洁。
  • 错误信息更丰富:可以携带字符串描述(如 “索引无效”),甚至自定义更多错误细节,比单纯的错误码(-1、0)更易读。
  • 强制调用者处理:若调用者未捕获异常,程序会直接终止并提示错误,避免 “错误被忽略” 导致后续逻辑异常(比如插入失败却继续访问非法位置)。

三、使用规范:避免踩坑的 3 个关键

1. 必须包含头文件(否则编译报错)

std::invalid_argument 定义在 <stdexcept> 头文件中,必须在代码开头引入:

cpp

运行

#include <stdexcept>  // 不可或缺!

2. 调用者必须捕获异常(否则程序崩溃)

异常抛出后,调用 insert 函数的代码必须用 try-catch 块捕获,否则程序会触发 terminate() 终止。

规范调用示例:

cpp

运行

int main() {

    SequentialList list;

    initList(&list);

    try {

        // 可能抛出异常的代码放在 try 块中

        insert(&list, 0, 10);  // 合法索引,正常执行

        insert(&list, 2, 20);  // 非法索引,抛出异常

    } catch (const std::invalid_argument& e) {  // 捕获特定异常类型

        // 处理异常:输出错误信息(e.what() 获取异常的字符串描述)

        std::cerr << “插入失败:” << e.what() << std::endl;

        // 可选:进行恢复操作(如释放资源、返回默认值等)

    }

    delete[] list.data;

    return 0;

}

  • 输出结果:插入失败:Invalid index
  • 注意:catch 时建议用 const 引用(const std::invalid_argument& e),避免异常对象的拷贝开销。

3. 异常类型要 “匹配场景”

C++ 标准库提供了多种异常类,需根据错误类型选择,避免混用:

异常类适用场景示例
std::invalid_argument参数本身非法(如索引格式错误)索引为负数、传入空指针参数
std::out_of_range参数超出合法范围(如索引越界)索引 > 顺序表 size、数组下标越界
std::bad_alloc内存分配失败(如 new 开辟空间失败)new eleType[1000000000] 失败

优化建议:你的场景更适合 std::out_of_range

顺序表插入时,索引非法的本质是「索引超出了合法范围」(合法范围:0 ≤ index ≤ size),用 std::out_of_range 更贴合语义,可读性更强:

cpp

运行

// 优化后:异常类型更精准

throw std::out_of_range(“Invalid index for insert: index out of valid range”);

四、进阶优化:让异常信息更 “有用”

原错误信息 “Invalid index” 过于简洁,调用者无法知道「具体哪个索引非法」「合法范围是什么」。可以补充上下文信息,方便调试:

cpp

运行

// 优化:携带具体索引值和合法范围

throw std::out_of_range(

    “Insert failed: invalid index = ” + std::to_string(index) + 

    “, valid range is [0, ” + std::to_string(list->size) + “]”

);

  • 错误信息示例:Insert failed: invalid index = 2, valid range is [0, 1]
  • 注意:std::to_string 用于将整数(index、list->size)转为字符串,需包含 <string> 头文件。

五、特殊情况:如果是 C 语言(无异常机制)

若你的代码实际是 C 语言(而非 C++),throw 和 std::invalid_argument 均不支持,此时需用「返回错误码 + 错误信息」替代:

c

运行

#include <stdio.h>

#define eleType int

// 顺序表结构体(C 语言版本)

typedef struct {

    eleType* data;

    int size;

    int capacity;

} SequentialList;

// 返回值:0 成功,-1 索引非法(C 语言无异常,用错误码表示)

int insert(SequentialList* list, int index, eleType element) {

    if (index < 0 || index > list->size) {

        // 打印错误信息到标准错误流(stderr)

        fprintf(stderr, “Insert failed: invalid index = %d, valid range is [0, %d]\n”, 

                index, list->size);

        return -1;  // 返回错误码

    }

    // 后续扩容、元素后移等逻辑…

    return 0;

}

// 调用示例

int main() {

    SequentialList list = {NULL, 0, 0};

    if (insert(&list, 2, 20) == -1) {

        // 处理错误(如终止程序、重试等)

        return 1;

    }

    return 0;

}

总结

  • 原代码 throw std::invalid_argument(“Invalid index”); 是 C++ 中「参数非法错误」的标准处理方式,核心是传递错误信息并强制调用者处理。
  • 规范用法:必须包含 <stdexcept> 头文件,调用者用 try-catch 捕获。
  • 优化方向:用 std::out_of_range 匹配索引越界场景,补充具体索引和合法范围让错误信息更清晰。
  • 兼容 C 语言:无异常机制时,用「返回错误码 + fprintf(stderr, …)」替代。

如果需要进一步优化异常处理(比如自定义异常类、统一错误处理机制),可以随时告诉我!

//Basic Data Structure Day2 Sequential List – Coding

//顺序表

#include <iostream>

using namespace std;

#define eleType int

//eleType 提升代码可维护性

//1、顺序表的定义

struct SequentialList {

eleType* elements; //建立一个指向eleType类型的数组的指针

int size; //顺序表当前的大小 元素的大小

int capacity; //顺序表的容量 数组的大小

};

//2、顺序表的初始化

void initializeList(SequentialList* list, int capacity) {

//initializeList是一个用于初始化顺序表的函数 接受一个SequentialList 类型的指针和一个整数作为参数

//结构体指针必须用箭头访问

list->elements = new eleType[capacity];//在函数内部通过new 运算符 分配一个具有指定容量的数组  并将其地址赋值给 list->elements 成员变量用来存储顺序表的元素

list->size = 0;//list的size初始化为0表示顺序表当前没有元素

list->capacity = capacity;//将list->capacity设为传递进来的容量值 用于记录顺序表的最大容量

}

//3、顺序表的销毁

void destroyList(SequentialList* list) {

delete[] list->elements; //destroyList是一个用于销毁顺序表的函数 接受了一个SequentialList类型的指针作为参数

//在函数内部使用 delete[]运算符释放了之前通过new分配的数组 以释放内存空间

}

//4、顺序表的元素个数

bool isEmpty(SequentialList* list) {

return list->size == 0; //检查顺序表是否为空

}

int size(SequentialList* list) {

return list->size; //获取顺序表的大小

}

//5、顺序表的元素插入

void insert(SequentialList* list, int index, eleType element) {

if (index < 0 || index > list->size) {

throw std::invalid_argument(“Invalid index”);//如果index小于0或大于list->size,则抛出一个std::invalid_argument异常 提示索引无效

}

if (list->size == list->capacity) {//如果顺序表已满 (list->size等于list->capacity),需要进行扩容操作

int newCapacity = list->capacity * 2;

eleType* newElements = new eleType[newCapacity];//计算新的容量,并通过new运算符分配一个新的数组

for (int i = 0; i < list->size; i++) {

newElements[i] = list->elements[i]; //将原数组的元素赋值到新数组中

}

delete[] list->elements; //回收原数组的内存空间

list->elements = newElements;

list->capacity = newCapacity;

}

for (int i = list->size; i > index; i–) {

list->elements[i] = list->elements[i – 1]; //将index之后的元素依次向后移动一个位置,为新元素腾出空间

}

list->elements[index] = element; //将新元素插入到指定的索引位置

list->size++; //将顺序表的大小增加1

}

//6.顺序表的元素删除

void deleteElement(SequentialList* list, int index) {

if (index < 0 || index >= list->size) {

throw std::invalid_argument(“Invalid index”); //如果index小于0或大于等于list->size,则抛出一个std::invalid_argument异常,提示索引无效

}

for (int i = index; i < list->size – 1; i++) {

list->elements[i] = list->elements[i + 1]; //将index之后的元素依次向前移动一个位置 覆盖原索引位置的元素

}

list->size–; //将顺序表的大小减少1

}

//7.顺序表的元素查找

int findElement(SequentialList* list, eleType element) {//定义一个名为findElement的函数,它接受一个SequentialList类型的指针和一个数据element作为参数

for (int i = 0; i < list->size; i++) { //使用一个for循环遍历顺序表所有元素,从索引0开始,到列表的大小减1(即list->size – 1)

if (list->elements[i] == element) { //在每次循环中检查当前元素是否等于给定的元素 如果相等返回该元素在列表中的索引

return i;

}

}

return -1; //如果循环结束后没有找到匹配的元素 返回-1表示未找到

}

//8.顺序表的元素索引

eleType getElement(SequentialList* list, int index) { //定义一个名为getElement的函数 它接受一个SequentialList类型的指针和一个整数作为参数

if (index < 0 || index >= list->size) {

throw std::invalid_argument(“Invalid index”); //检查索引是否小于0或大于等于列表的大小 如果是抛出一个std::invalid_argument 异常 ,提示索引无效

}

return list->elements[index]; //如果索引有效,返回列表中索引位置的元素

}

//9.顺序表的元素修改

void updateElement(SequentialList* list, int index, eleType value) {//定义一个名为updateElement的函数,它接受一个SequentialList类型的指针、一个整数索引和一个值作为参数

if (index < 0 || index >= list->size) { //检查索引是否小于0或大于等于列表的大小,如果是,抛出一个std::invalid_argument异常,提示索引无效

throw std::invalid_argument(“Invalid index”);

}

list->elements[index] = value; //如果索引有效,将列表中指定索引处的元素更新为给定的值

}

int main() {

SequentialList myList;

initializeList(& myList, 10);

for (int i = 0; i < 10; i++) {

insert(&myList, i, i * 10);

}

cout << “Size:” << size(&myList) << endl;

cout << “Is empty:” << isEmpty(&myList) << endl;

for (int i = 0; i < size(&myList); i++) {

cout << getElement(&myList, i) << ” “;

}

cout << endl;

deleteElement(&myList, 5);

updateElement(&myList, 1, 1314);

int idx = findElement(&myList, 20);

updateElement(& myList, idx, 520);

for (int i = 0; i < size(&myList); i++) {

cout << getElement(&myList, i) << ” “;

}

cout << endl;

destroyList(&myList);

return 0;

}