#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;
}