C++ —— 链表

C++ —— 简单链表

结构体

先看一段有关结构体的代码

#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

感谢浏览

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值