为什么不写&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
也就是说:
- & (mylist.size) 取的是 size 这个成员变量本身的地址,类型是 int*
- 然后 – 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;
这样做的意义:
- 明确状态:告诉自己/别人:这张表现在没有有效数组了
- 避免误用:以后如果不小心用 elements,至少能更容易发现问题(比如你可以先判断是不是 nullptr)
- 防止重复释放:如果你不小心又调用一次 destroyList,delete[] nullptr; 是安全的(不会崩)
nullptr 和 NULL / 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;
}