C++学习笔记#01——指针与链表

在自学C++的时候,发现指针是一个很难绕开的东西,看了一些参考资料和别人的程序,写一些垃圾。

Part 1 指针

指针是一个指向一片内存地址的变量,相当于家的门牌号。我们即可以通过变量名来访问一个变量,也可以通过它对应的地址来访问。就像你的老师可以点你的名字找你,也可以找你宿舍的门牌号来找到你()。

指针的基本操作——创建,赋值

创建与赋值

在C++中,指针通过一个 “*” 定义,例如:

int *ptr;
int* ptr;

这两种方法都是可行的。需要注意的是,这时侯的指针没有被初始化,是一个“野指针”,访问其会导致不可预期的问题。因此,我们要对其进行初始化:

ptr = nullptr;

(在C++11标准前,初始化为NULL)

或者,假如我们有一个变量val:

ptr = &val;

其中,“&”为取地址运算符,把这个变量的地址赋给这个指针,而不是这个变量本身,否则将导致未定义行为。

一个int类型的指针不仅可以指向一个int,还可以指向一个int类型的数组。

int arr[5];
ptr = &arr;

此时,指针ptr指向arr数组第一个(即索引为0)的地址。

亦或者,我们可以new运算符,为这个指针分配一个新的内存

int ptr = new int;

这样,指针ptr就指向了一片新的内存空间,大小为一个int(4字节)。

此外,我们也可以使用指针来创建并管理一个数组。

int *arr = new int[5];

这样就得到了一个arr数组,它的操作和普通的数组没有区别:

//对数组进行赋值并输出
for(int i = 0; i < 5; i++){
        arr[i] = i;
    }
    for(int i = 0; i < 5; i++){
        cout << arr[i] << " ";
    }

基本操作——(解)引用,移动

指针的解引用是指通过指针变量访问其指向的内存地址所存储的数据。指针本身存储的是内存地址,而解引用操作则是获取该地址中存储的实际数据。
解引用操作符:*
用法:

*ptr //需要搭配其它的操作,否则没有意义

我们可以使用+=,-=等运算符对内存的数据进行修改,如:

*ptr += 1

*ptr += 1 与 ptr += 1?——指针的移动

我们必须要注意的是,ptr += 1 与 ptr += 1是两种完全不同的运算。前者表示修改内存地址的数据,而后者表示修改指针指向的内存地址,将内存地址向后移动一定字节,具体取决于数据类型,如一个int4字节,一个char1字节,等等。
此外,ptr++,
ptr++与ptr += 1是一样的操作。
这样,我们就可以使用指针对像数组这样连续的内存进行访问。

//假定已有数组arr[],大小为5,内容为{0,1,2,3,4}
    int *ptr3 = arr;
    int cnt = 0;
    while(cnt < 5){
        cout << *ptr3 << " ";
        cnt++;  ptr3++;//与ptr+=1等价
    }

运行,输出为:

0 1 2 3 4

如果我们将cnt < 5改为cnt < 10,再次运行:

0 1 2 3 4 1868770670 754697088 61231 17653600 0

显然,程序输出了无法预期的结果。
我们在实操时必须时刻注意,以免访问无效内存,造成运行崩溃或者难以预期的错误。

Part 2 链表

链表,需要一种类型的变量,它能同时记载当前节点的值与下一个节点的地址。

链表的基本功能实现

在C++中可以使用class语句来定义这个类。

class node{
    public:
        int data;
        node* next;
        node(int val) : data(val), next(nullptr) {}
};

值得注意的是,构造函数在每个节点生成是都将下一个节点的指针初始化为nullptr,这样我们就不需要在添加/插入节点时增加代码了。
链表的添加(尾部)

void append(int val){   //表尾插入
            node* newnode = new node(val);
            if(head == nullptr){
                head = newnode;
            }else{
                node* cur = head;
                while(cur->next != nullptr){
                    cur = cur->next;
                }
                cur->next = newnode;
            }
        }

在值为a的节点后面插入

void insert(int a, int val){    //在值为a的节点后面插入
            node* cur = head;
            node* newnode = new node(val);
            while(cur->next != nullptr){
                if(cur->data == a){
                    newnode->next = cur->next;
                    cur->next = newnode;
                    return;
                }
                cur = cur->next;
            }
            if(cur->next == nullptr && cur->data == a){//在最后面插入
                append(val);
                delete newnode;
            }
        }

删除值为x的节点
在将某个节点从链表上“断开”后,需要对这块内存进行释放,因此我们定义临时指针变量tmp记录其地址,便于后续操作。

void delete(int x){
            node* cur = head;
            while(cur->next != nullptr){
                if(cur->next->data == x){
                    node* tmp = cur->next;
                    cur->next = cur->next->next;
                    delete tmp;
                    tmp = nullptr;
                    return;
                }
                cur = cur->next;
            }
        }

查找链表上某个值为下的节点后面节点的值
可能要找的节点不存在或者恰好为最后一个节点,这时返回-1或0。

int findnext(int x){    //查找值为x的节点后面的那个节点的值
            node* cur = head;
            while(cur->next != nullptr){
                if(cur->data == x){
                    return cur->next->data;
                }
                cur = cur->next;
            }
            if(cur->next == nullptr && cur->data == x){
                return 0;
            }else{
                return -1;
            }
        }

———————————————持续更新中———————————————

posted @   林石Linstone  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示