C进阶指针(3)----NULL指针

过了几天,小明又找到博主,问之前计算器程序中的NULL指针是什么,虽然杰哥讲过,但没怎么听懂。

既然这样,我们就只好为小明再开一篇文章,探究以下NULL指针。

什么NULL指针?

以下选自某度某科

 C语言的NULL指针是什么?

其实C语言的NULL指针和某度某科所说的,是大同小异的,区别很小的,NULL就是一个指向了空值,但是注意,NULL不是直接定义成了一个常量0,而是(void *)0,这是因为任何类型的指针变量赋值NULL时都能够赋值0,我们来看一下定义就明白了

NULL的定义(VS2022)

 #define NULL ((void *)0)

这样定义的好处是假设我有一个字符型指针ch,我们需要让他指向内存地址为0的地址时,就可以为他赋值NULL指针,在假设,我们定义一个整形指针变量p,我们也需要让他指向内存地址为0的地址,那么如果NULL单纯是常量0的话,就不能完美让字符型指针ch接收,依次类推,所以,为了兼容所有类型的变量,这里就引入了万能指针的概念,即(void *)指针。

这时候小明就又有疑问了,什么是万能指针(void *)?

那么有关这部分内容我也会出单独一篇文章来阐述,这里篇幅有限,不过多解释。

虽然小明没得到答案,但还是认为吧NULL指针给搞明白比较好,于是问博主能不能以代码案例来讲解

当然可以,案例1

我们有以下代码

#define _crt_secure_no_warnings 1
#include<stdio.h>
void test()
{
	printf("Hello world!\n");


}
int main()
{
	char a = 'a';
	char* ch;
	ch = &a;
	ch = NULL;
	printf("%c\n", ch);

}

这段代码平平无奇,但却能很好的说明,为什么NULL要定义为(void *)

首先,我们打开VS2022,写下以上代码,按“F10”进入VS2022的调试模式,按,F10一直到如图所示       

 让左侧小箭头指向ch = NULL;这行代码,因为,此时我们检查以下此时ch指向a时的地址是多少。

然后,我们找到VS2022菜单栏中的“调试->窗口->内存->内存1”,如图所示

 即可查看每个变量的实时地址,以上操作无误,应是下图所示

然后,我们在“地址”搜索栏中输入“ch”,按回车键,得道此时ch的地址,如下图所示

 

我们可以发现,此时的ch的指向0x000000B24A0FF620,即a变量的地址。

记录数据,以方便我们一会与NULL指针作比较

接下里,我们继续按“F10”,一直到如图所示

 

 注意:不要按过头了,导致调试结束,从而退出程序,这时候就无法使用内存功能,因为只有在调试模式下才能使用内存模式

重复找内存功能的操作,打开内存1

 这次,继续在“地址”搜索栏中输入“ch”,按回车键,得到此时的NULL指针,如图所示

 

此时,我们居然发现,ch指向0地址,即常量0的地址,若ch为int类型,最终结果也是如此,所以,这也是为什么吧NULL定义为(void *)。

这时候,问题超多的小明又问了一个问题

那NULL指针可以解引用吗?

不可以!!!

因为NULL指针的地址时0x0000000000000000,这也是操作系统的存放0的地址,为了保护系统,所以,NULL指针只可以被指向,而不可以解引用!

### 链表的基本操作 #### 创建链表 链表是一种动态数据结构,可以通过节点来表示其基本单元。每个节点包含两部分:存储的数据和指向下一个节点的指针。以下是创建单向链表的一个简单示例: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node* next; } Node; Node* create_node(int value) { Node* new_node = (Node*)malloc(sizeof(Node)); if (!new_node) { printf("内存分配失败\n"); exit(1); } new_node->data = value; new_node->next = NULL; return new_node; } ``` 上述代码展示了如何通过 `create_node` 函数创建一个新的节点[^6]。 --- #### 向链表追加元素 为了向已有的链表中追加新元素,可以从头节点开始遍历整个链表直到找到最后一个节点(即 `next` 指针为 `NULL` 的节点)。然后将新的节点连接到最后一个节点上。 ```c void append_node(Node** head_ref, int value) { Node* new_node = create_node(value); if (*head_ref == NULL) { *head_ref = new_node; return; } Node* last = *head_ref; while (last->next != NULL) { last = last->next; } last->next = new_node; } ``` 此函数实现了在链表末尾添加新节点的功能[^7]。 --- #### 输出链表的内容 要输出链表中的所有元素,只需从头节点开始依次访问每一个节点并打印其中的数据字段即可。 ```c void print_list(Node* node) { while (node != NULL) { printf("%d -> ", node->data); node = node->next; } printf("NULL\n"); } ``` 这段代码用于逐个打印链表中的所有节点及其对应的数值[^8]。 --- #### 完整示例程序 下面是一个完整的 C 程序,演示了如何创建链表、向链表追加元素以及输出链表内容。 ```c #include <stdio.h> #include <stdlib.h> // 节点定义 typedef struct Node { int data; struct Node* next; } Node; // 创建节点 Node* create_node(int value) { Node* new_node = (Node*)malloc(sizeof(Node)); if (!new_node) { printf("内存分配失败\n"); exit(1); } new_node->data = value; new_node->next = NULL; return new_node; } // 追加节点 void append_node(Node** head_ref, int value) { Node* new_node = create_node(value); if (*head_ref == NULL) { *head_ref = new_node; return; } Node* last = *head_ref; while (last->next != NULL) { last = last->next; } last->next = new_node; } // 打印链表 void print_list(Node* node) { while (node != NULL) { printf("%d -> ", node->data); node = node->next; } printf("NULL\n"); } int main() { Node* head = NULL; append_node(&head, 1); append_node(&head, 2); append_node(&head, 3); print_list(head); return 0; } ``` 该程序展示了一个简单的链表管理流程,包括初始化、追加节点和打印功能[^9]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值