首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C语言入门知识点(11.指针c篇)(超详细)

C语言入门知识点(11.指针c篇)(超详细)

作者头像
say-fall
发布2026-01-15 10:00:17
发布2026-01-15 10:00:17
220
举报

  • 🌈🌈🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言
  • 🔥🔥🔥专栏:《C语言入门知识点》、《C语言底层》
  • 💪💪💪格言:今天多敲一行代码,明天少吃一份苦头

前言:

本篇继续补充指针篇的知识点,包括指针运算、野指针(包含悬垂指针)、assert断言和指针传参,感兴趣的小伙伴们快做好笔记!!!


正文:

5. 指针运算

指针基本运算包括三种:

  • 指针 ± 整数
  • 指针 - 指针
  • 指针的关系运算
5.1 指针 ± 整数

在前文《指针b篇》我们已经了解过指针 ± 整数(了解详情可点击《指针b篇》回顾),我们知道pointer ± n*sizeof(----)----代表的是变量类型,而且变量类型对应了指针会移动几个字节。

那有什么应用呢?

我们不妨想一下,有哪些数据在内存中是连续存放的?很显然,数组就是这样。 也就是说,我们可以通过指针来访问数组的元素,好,详看代码:

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9};
	int* p = &arr[0];
	printf("%d\n", arr[2]);
	printf("%d\n", *(p + 2));
	return 0;
}

运行结果:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
	printf("%d\n", arr[2]);
	printf("%d\n", *(p + 2));
  • 可以看得到,打印的两个结果是完全相同的。也就是说,指针通过+整数(移动了4个字节)的方式,访问到了数组的元素。

跑题一下,我们了解一个数组和指针的“小秘密”: 其实数组名就是一个数组第一个元素的指针 即int* arr = arr[0]

验证一下:

代码语言:javascript
复制
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%d ", *arr);
	return 0;
}

运行结果:

在这里插入图片描述
在这里插入图片描述

既然如此的话,我们发现数组其实有两种表示方法:

  • 1.指针表示法
  • 2.数组表示法

指针表示法其实相对来说更底层一点,因为它对内存的应用更明显。

5.2 指针 - 指针

明白了指针与整数的运算,不妨先来猜一下指针 - 指针实际上是在做什么吧

先贴一段代码,想想他的运行结果

代码语言:javascript
复制
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int x = 0;
	x = *(arr + 4) - *(arr + 1);
	printf("%d\n", x);
	return 0;
}

揭晓答案,运行结果:

在这里插入图片描述
在这里插入图片描述

答案是3,也就是说指针 - 指针的结果是指针与指针之间所差变量类型的个数

代码语言:javascript
复制
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int x = 0;
	x = *(arr + 4) - *(arr + 1);
	printf("%d\n", x);
	int arr_ch[6] = { 'a','b','c','d','e','f' };
	int y = 0;
	y = *(arr_ch + 4) - *(arr_ch + 1);
	printf("%d\n", y);
	return 0;
}

运行结果;

在这里插入图片描述
在这里插入图片描述

看来确实如此。

有什么应用吗?

我记得有一个函数,能返回字符串的长度。strlen() 我们来试着复刻一下这个函数:

代码语言:javascript
复制
int my_strlen(char* s)
{
	char *x = s;
	while(*x !=  '\0')
		x++;
	return x - s;
}

int main()
{
	printf("%d", my_strlen("abcd"));
	return 0;
}

运行结果:

在这里插入图片描述
在这里插入图片描述

yes!我们成功了!

5.3 指针的关系运算

我们这次直接来看代码

代码语言:javascript
复制
//指针的关系运算 
#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较 
 {
 printf("%d ", *p);
 p++;
 }
 return 0;
}

这段代码中就用到了指针间的大小比较,通过这串代码输出了数组的元素:

在这里插入图片描述
在这里插入图片描述
6. 野指针
6.1 初始化指针和野指针

首先来说一下初始化指针,当我们知道这个指针是指向那里的时候,我们一般直接给指针一个地址,如果不知道的话我们给指针一个NULLNULL和0是等价的,也就是让这个指针指向0。而如果我们什么都不初始化,直接定义一个指针而不赋值,在这个指针就被称为野指针,有的时候指针在运行时跑出作用范围也被称为野指针。

6.1.1 初始化指针
代码语言:javascript
复制
#include <stdio.h> 

int main()
{
	int* p0 = NULL;
	int* p1 = 0;
	int* p2;
	printf("%p\n", p0);
	printf("%p\n", p1);
	printf("%p\n", p2);
	return 0;
}

Dev c++运行结果:

在这里插入图片描述
在这里插入图片描述

VS 2022运行结果:

在这里插入图片描述
在这里插入图片描述
6.1.2 指针越界访问

代码来啦!

代码语言:javascript
复制
#include <stdio.h>
int main()
{
 int arr[10] = {0};
 int *p = &arr[0];
 int i = 0;
 for(i=0; i<=11; i++)
 {
 //当指针指向的范围超出数组arr的范围时,p就是野指针 
 *(p++) = i;
 }
 return 0;
}

这个数组的范围为10,而指针访问时最后到达12才停止,导致报错:

在这里插入图片描述
在这里插入图片描述
6.1.2 指针越界访问

代码来啦!

代码语言:javascript
复制
#include <stdio.h>
int* test()
{
	int n = 100;
	return &n;
}
int main()
{
	int* p = test();
	printf("%d\n", *p);
	return 0;
}

代码一结果:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
#include <stdio.h>
int* test()
{
	int n = 100;
	return &n;
}
int main()
{
	int* p = test();
	printf("%p\n", p);
	printf("%d\n", *p);
	return 0;
}

代码二结果:

在这里插入图片描述
在这里插入图片描述

已知test();返回的是 &n ,但是n是在test()内部的局部变量,如果在test()之外使用&n(p),此时的p是一个悬垂指针,返回的是一个无效的地址,打印的值又可能是100;也有可能是随机值;也可能导致程序崩溃(内存非法访问)。

  • 第2、3种方法都会产生悬垂指针
6.2 如何规避野指针

其实上面的三种野指针成因也就对应了规避野指针的方法

6.2.1 初始化野指针时赋值或置于NULL
代码语言:javascript
复制
#include <stdio.h> 

int main()
{
   int* p0 = NULL;
   int* p1 = 0;
   printf("%p\n", p0);
   printf("%p\n", p1);
   return 0;
}
6.2.2 小心指针越界

在调整指针指向的位置时,不要让指针超出访问的空间,超出空间时,指针就越界访问了

6.2.3 指针不再使用时,及时置0,使用前检查指针有效性

在使用指针后,指针停止使用,再次使用时检查。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
 int arr[10] = {0};
 int *p = &arr[0];
 int i = 0;
 for(i=0; i<=11; i++)
 {
 //当指针指向的范围超出数组arr的范围时,p就是野指针 
 *(p++) = i;
 }
 //此时p已经越界了,可以把p置为NULL 
 p = NULL;
 //下次使⽤的时候,判断p不为NULL的时候再使⽤ 
 //...
 p = &arr[0];//重新让p获得地址 
 if(p != NULL) //判断 
 {
 //...
 }
 return 0;
}
6.2.4 避免指针返回局部变量地址

像是上面的例子,指针不能返回局部变量地址,会产生不可预料的结果。

7. assert断言

我们上文中提到了判断指针p是否置于NULL,如果为真就继续执行,不为真就不执行,这里我们介绍一种用于判断然后决定是否继续运行的宏:assert()assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运⾏。这个宏常常被称为“断⾔”。

  • PS:宏:是由编译器或解释器预处理的代码替换规则,在程序运行前(预处理阶段)会被自动替换为定义好的代码片段。

我们在这里这样用assert():

代码语言:javascript
复制
#include <assert.h>
assert(p != NULL);

如果想要开启或者关闭assert()函数呢,我们就在#include assert()前定义一个DNEBUG

代码语言:javascript
复制
#define DNEBUG
#include <assert.h>

这样的话,下次运行函数的时候,就会自动屏蔽掉assert(),同理删除后在运行就能开启assert()

assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。

⼀般我们可以在 Debug 中使⽤,在 Release 版本中选择禁⽤ assert 就⾏,在 VS 这样的集成开 发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题, 在 Release 版本不影响⽤⼾使⽤时程序的效率

8. 指针的使用与传址调用
8.1 strlen的模拟实现

前文见过,代码走起!

代码语言:javascript
复制
int my_strlen(char* s)
{
	char *x = s;
	while(*x !=  '\0')
		x++;
	return x - s;
}

int main()
{
	printf("%d", my_strlen("abcd"));
	return 0;
}

运行结果依旧正确:

在这里插入图片描述
在这里插入图片描述
8.2 传值调用与传址调用

我们不用指针写一个交换数值的函数:

代码语言:javascript
复制
#include <stdio.h>
void Swap1(int x, int y)
{
 int tmp = x;
 x = y;
 y = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap1(a, b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

运行一下:

在这里插入图片描述
在这里插入图片描述
  • 欸,为啥没换过来

试试调试:

在这里插入图片描述
在这里插入图片描述
  • 可以看得到,在出函数的前一刻,xy的地址和ab的地址是不一样的,出函数以后,xy作为局部变量就会被销毁,也就导致了虽然x和y交换数值了,但是和ab有什么关系呢?

上面我们用的是函数的传值调用,试试用指针呢

代码语言:javascript
复制
#include <stdio.h>
void Swap2(int*px, int*py)
{
 int tmp = 0;
 tmp = *px;
 *px = *py;
 *py = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap2(&a, &b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

能看的到和上次不同,我们这次调用的是地址,这样的话直接让地址被交换(即指针),就能交换ab的值了。 运行结果:

在这里插入图片描述
在这里插入图片描述

很好,通过两个函数的对比,我们就了解了什么时候必须要用指针,这也就大大提高了指针存在的必要性。

  • 本节完…
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 正文:
    • 5. 指针运算
      • 5.1 指针 ± 整数
      • 5.2 指针 - 指针
      • 5.3 指针的关系运算
    • 6. 野指针
      • 6.1 初始化指针和野指针
      • 6.2 如何规避野指针
    • 7. assert断言
    • 8. 指针的使用与传址调用
      • 8.1 strlen的模拟实现
      • 8.2 传值调用与传址调用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档