结构体
先看一段有关结构体的代码
#include <iostream>
#include <cstring>
using namespace std;
struct st_t
{
int a;
int* p;
};
int main () {
st_t stt;
stt.a = 666;
stt.p = new int[100];
cout << "sizeof(stt): " << sizeof(stt) << endl;
cout << "stt.a = " << stt.a << ", stt.p = " << stt.p << endl;
memset(&stt, 0, sizeof(st_t));
cout << "stt.a = " << stt.a << ", stt.p = " << stt.p << endl;
delete [] stt.p;
return 0;
}
运行结果如下:
sizeof(stt): 16
stt.a = 666, stt.p = 0x557206147eb0
stt.a = 0, stt.p = 0
可以看到调用memset()
函数后,stt.a
整型变量a
的值是0
,整型指针stt.p
的值是空地址,并不是一个合法的地址。成员p
是指针,存放了动态分配出来的内存的地址。在C++
中,用指针
跟踪动态
分配的内存,如果要使用这块内存,只能通过指针p
。调用memset()
函数后,指针p
被清零了。这样做会产生两个后果:
- 如果程序中继续对指针
p
进行操作,就是操作空指针
; - 指针
p
指向的那块内存没有
被释放
,产生了内存泄漏
;最后的结果是:堆中的内存越来越少,整个系统越来越慢,程序也会崩溃。
如何解决
在没有动态分配内存之前,是可以用memset()
函数清空结构体的。在动态分配内存之后,就不能用memset()
函数清空整个结构体了,只能逐个成员处理。修改后的代码如下:
int main () {
st_t stt;
memset(&stt, 0, sizeof(st_t));
stt.a = 666;
stt.p = new int[100];
cout << "sizeof(stt): " << sizeof(stt) << endl;
cout << "stt.a = " << stt.a << ", stt.p = " << stt.p << endl;
stt.a = 0; // 清空成员a
memset(stt.p, 0, 100*sizeof(int)); // 清空指针p指向的内存中的内容(清空内存中的内容),不是把p置为0
cout << "stt.a = " << stt.a << ", stt.p = " << stt.p << endl;
delete [] stt.p;
return 0;
}
运行结果如下:
sizeof(stt): 16
stt.a = 666, stt.p = 0x56222c57eeb0
stt.a = 0, stt.p = 0x56222c57eeb0
如果结构体中的指针指向的是动态分配的内存地址:
- 对结构体运用
sizeof()
运算可能没有意义 - 对结构体用
memset()
可能会造成内存泄漏 C++
字符串string
中有一个指向的是动态分配的内存地址指针
string
是类,在C++
中,结构体和类是同一回事。
链表
结构体的成员不能是自己,但可以是自己得指针,这种结构体体现了数据元素之间的链式关系,叫链表
。链表
中的每个元素叫节点
,每个节点
包括自身数据、next
指针(存放下一个节点的地址),如果是最后
一个节点,next
就空
着,表示没有下一个节点。链表
有单链表
、双链表
。
单链表
:节点间单向联系
;双链表
:节点间双向联系
。
示例代码如下:
#include <iostream>
using namespace std;
struct car
{
int num;
string brand;
struct car* next;
};
int main () {
struct car* head = nullptr; // 链表的头指针,用于存放 first node 的地址(必须要有)
struct car* tail = nullptr; // 链表尾指针,用于存放 last node 的地址
struct car* tmp = nullptr; // 临时节点指针,用于遍历链表
// 链表中每一个 node 都是动态分配的,数组 array 是静态分配的
// 链表新增节点时,就分配一块内存;删除节点时,就释放这块内存
// 分配内存,让 tmp 指针指向这块内存
tmp = new car;
// 给新分配的节点赋值
tmp->num = 1;
tmp->brand = "Tesla";
tmp->next = nullptr; // 新分配的节点的 next 指针一般为 nullptr,若需要指向某个节点,则赋值
// 分配好第一个节点后,将 head 和 tail 指针指向这个节点
head = tail = tmp;
tmp = new car({2, "理想", nullptr}); // 分配 second node
// 上一个 node 的 next 指针指向新节点 上一个 node 就是 tail 指向的节点
tail->next = tmp;
tail = tmp; // tail 指针指向刚分配的节点
tmp = new car({3, "Porsche", nullptr}); // 分配第三个节点
tail->next = tmp;
tail = tmp; // tail 指针指向刚分配的节点
// traverse 链表 从 head 开始,沿着 next 往后找,直到 next 为 nullptr
tmp = head;
while (tmp != nullptr) {
cout << "No." << tmp->num << " " << tmp->brand << " next:" << tmp->next << endl;
tmp = tmp->next;
}
// 链表用完了,释放内存
while (head != nullptr) {
tmp = head; // tmp 指向 head
head = head->next; // head 往后移动
delete tmp; // 删除 tmp
}
return 0;
}
运行结果:
No.1 Tesla next:0x55dee4c4fef0
No.2 理想 next:0x55dee4c4ff30
No.3 Porsche next:0
感谢浏览