#C++ Day43 Basic Data Structure Chapter3  review December 20 2025

为什么不写&mylist.size-1

因为 deleteElement 的第二个参数要的是 int 下标,不是“地址”。

你的函数声明是:

void deleteElement(SequentialList* list, int index)

  • 第 1 个参数 SequentialList*:要传顺序表的地址,所以要写 &mylist
  • 第 2 个参数 int index:要传下标这个数字,所以要写 mylist.size – 1

你写 &mylist.size-1 会发生什么?

先看运算优先级:. 的优先级比 & 高,所以

&mylist.size-1

等价于:

(& (mylist.size)) – 1

也就是说:

  1. & (mylist.size) 取的是 size 这个成员变量本身的地址,类型是 int*
  2. 然后 – 1 是指针运算,得到另一个地址(仍然是 int*)

结果类型是 int*,而函数需要的是 int,类型完全不匹配,编译就会报错。

更关键的一点:就算语法上能传进去,也没意义

deleteElement 需要的是“要删哪个位置”的编号,例如 9、19、0。
而 &mylist.size 是“size 这个整数在内存里放在哪儿”的地址,它跟“最后一个元素的下标”不是一回事。

所以正确写法只能是:

deleteElement(&mylist, mylist.size – 1);

如果你想试试验证,可以把 &mylist.size 打印出来(作为地址)看看,它会是类似 0x7ffee… 这种,不可能是合理的下标数字。

你现在这个“怎么办”,有两条路选,取决于你想不想让程序在 destroyList 之后还继续“安全地用这个表”。

方案 A:不改代码(只改用法)

你的代码里 destroyList 的语义就是:释放底层数组
所以你要遵守一条规则:

调用 destroyList(&mylist); 之后,就不要再把 mylist 当成可用的顺序表来用。
只能结束程序,或者重新 initializeList 之后再用。

也就是说,你这段:

destroyList(&mylist);

cout << isEmpty(&mylist) << endl;

cout << sizeOfList(&mylist) << endl;

从“概念上”是不合理的,因为 destroy 之后这个结构体已经处于“被销毁但还没重建”的状态。
(它的 size 还在,但 elements 指向的内存已经没了。)

如果你只是想“验证 destroy 后发生了什么”,你可以打印 size,但要理解:这不是一个可用状态

方案 B:让 destroy 后变成“干净的空表”(需要改一小点)

你现在的问题本质是:destroy 只 delete[],但没有把 elements/size/capacity 变成一个“明确的无效/空状态”。

你可以让 destroyList 在释放后把它清空:

void destroyList(SequentialList* list) {

    delete[] list->elements;

    list->elements = nullptr;

    list->capacity = 0;

    list->size = 0;

}

这样 destroy 之后:

  • sizeOfList 会返回 0
  • isEmpty 会返回 true
  • 并且 elements=nullptr 也能避免“误用旧指针”和“重复 destroy 导致二次释放”的风险

注意:这并不等于“还能继续 insert/get”。
如果你要继续用,仍然要先 initializeList 重新分配数组。

你现在该怎么选?

  • 你只是写作业/练习生命周期:选 A,destroy 后不再使用它。
  • 你希望 destroy 后状态更合理、更不容易踩坑:选 B,destroy 后顺便把成员重置。

你想要的是哪一种:destroy 后彻底不能用(A),还是 destroy 后变成“空壳”但要重新初始化才能用(B)?我可以按你选的那种,帮你把“正确的使用顺序”用你 main 里的流程给你标出来。

nullptr

nullptr 是 C++11 引入的一个关键字,表示**“空指针”**:也就是“这个指针不指向任何有效对象/内存”。

在你的结构体里:

eleType* elements;

elements 是一个指针。你 destroyList 里 delete[] list->elements; 之后,这个指针还保存着旧地址,但那块内存已经释放了,此时它变成了“悬空指针”(dangling pointer)。这很危险,因为你之后如果再用它,会访问到已经无效的内存。

所以常见习惯是释放后把它设为空指针:

list->elements = nullptr;

这样做的意义:

  1. 明确状态:告诉自己/别人:这张表现在没有有效数组了
  2. 避免误用:以后如果不小心用 elements,至少能更容易发现问题(比如你可以先判断是不是 nullptr)
  3. 防止重复释放:如果你不小心又调用一次 destroyList,delete[] nullptr; 是安全的(不会崩)

nullptrNULL / 0 有什么区别?

  • NULL 在很多实现里其实就是 0 或 0L,本质是整数,容易引发重载歧义。
  • nullptr 是专门的“空指针类型”(std::nullptr_t),只用于指针,更安全。

所以 C++ 里推荐用 nullptr,别用 NULL。

你在当前代码里,nullptr 应该怎么理解?

如果你将来(概念上)让 destroy 后做:

list->elements = nullptr;

就等于把 elements 变成“没有指向任何数组”的状态。

注意:nullptr 只适用于指针成员(比如 elements),不适用于 size/capacity 这种 int。

如果你愿意,我也可以结合你这份代码,告诉你“什么时候 elements 该是 nullptr,什么时候不该是 nullptr”,这样你更容易判断顺序表现在是不是可用状态。

#include <iostream>

using namespace std;

#define eleType int

//1.define

struct SequentialList {

eleType* elements;

int capacity;

int size;

};

//2.initialize

void initializeList(SequentialList* list, int capacity) {

list->elements = new eleType[capacity];

list->capacity = capacity;

list->size = 0;

}

//3.destroy

void destroyList(SequentialList* list) {

delete[] list->elements;

list->elements = nullptr; //c++11里面引入的一个关键字 等于把elements变成没有指向任何数组的状态 nullptr只适用于像elements这种指针成员 不适用于 size/capacity 这种int

list->size = 0;

list->capacity = 0;

//置空

}

//4.isempty and size

bool isEmpty(SequentialList* list) {

return list->size == 0;

}

int sizeOfList(SequentialList* list) {

return list->size;

}

//5.insert

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

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

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

}

if (list->capacity == list->size) {

int newcapacity = list->capacity * 2;

eleType* newlist = new eleType[newcapacity]; //返回值是这个new数组的首地址 所以写eleType * newlist

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

newlist[i] = list->elements[i];

}

delete[] list->elements;

list->elements = newlist;

list->capacity = newcapacity;

}

/*list->size++;

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

list->elements[i + 1] = list->elements[i];

}

list->elements[index] = element;*/ // 有BUG 插入函数的数据吞噬漏洞

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

list->elements[i] = list->elements[i – 1];

}

list->elements[index] = element;

list->size++;

}

//6.delete

void deleteElement(SequentialList* list, int index) {

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

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

}

/*for (int i = index; i < list->size; i++) {

list->elements[i] = list->elements[i + 1];

}

list->size–;*///有数组越界 bug list->size-1才对 不然读size可能会读到其他值例如密码

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

list->elements[i] = list->elements[i + 1];

}

list->size–;

}

//7.find

int findElementIndex(SequentialList* list, eleType element) {

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

if (list->elements[i] == element) {

return i;

}

}

return -1;

}

//8.get

eleType getElement(SequentialList* list, int index) {

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

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

}

return list->elements[index];

}

//9.update

void updateElement(SequentialList* list, int index, eleType value) {

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

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

}

list->elements[index] = value;

}

int main() {

SequentialList mylist;

int mycapacity=10;

initializeList(&mylist, mycapacity);

cout << “IsEmpty:” << isEmpty(&mylist)<<endl;

cout << “Size:” <<sizeOfList(&mylist) << endl;//初始化时 size是置于零的 所以没有size

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

insertElement(&mylist, i, i * 10);

}

cout << “IsEmpty:” << isEmpty(&mylist) << endl;

cout << “Size:” << sizeOfList(&mylist) << endl;

for (int i = 0; i < sizeOfList(&mylist); i++) {

cout << getElement(&mylist, i) << ‘ ‘;

}cout << endl;

int myindex = findElementIndex(&mylist, 50);

updateElement(&mylist,myindex,520);

for (int i = 0; i < sizeOfList(&mylist); i++) {

cout << getElement(&mylist, i) << ‘ ‘;

}cout << endl;

int i = 10;

while(i){

insertElement(&mylist, sizeOfList(&mylist), 555);

i–;

}

for (int i = 0; i < sizeOfList(&mylist); i++) {

cout << getElement(&mylist, i) << ‘ ‘;

}cout << endl;

deleteElement(&mylist, mylist.size-1);

for (int i = 0; i < sizeOfList(&mylist); i++) {

cout << getElement(&mylist, i) << ‘ ‘;

}cout << endl;

cout << “IsEmpty:” << isEmpty(&mylist) << endl;

cout << “Size:” << sizeOfList(&mylist) << endl;

deleteElement(&mylist, 2);

insertElement(&mylist, 0, 5);

for (int i = 0; i < sizeOfList(&mylist); i++) {

cout << getElement(&mylist, i) << ‘ ‘;

}cout << endl;

cout << “IsEmpty:” << isEmpty(&mylist) << endl;

cout << “Size:” << sizeOfList(&mylist) << endl;

destroyList(&mylist);

cout << “IsEmpty:” << isEmpty(&mylist) << endl;

cout << “Size:” << sizeOfList(&mylist) << endl;

return 0;

}