首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言指针二】从入门到通透:核心知识点全梳理(野指针,assert断言,指针的使用和传址调用,数组名的理解和使用指针反访问数组)

【C语言指针二】从入门到通透:核心知识点全梳理(野指针,assert断言,指针的使用和传址调用,数组名的理解和使用指针反访问数组)

作者头像
用户11987028
发布2026-01-15 14:02:13
发布2026-01-15 14:02:13
500
举报

-

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

🎬 个人主页秦苒&专栏传送门:《C语言

🍀指尖燃热血,代码铸锋芒;以信仰破局,向顶峰生长


🎬秦苒&的简介:

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

前言:前面在【C语言指针一】从入门到通透:核心知识点全梳理(内存、变量、运算、const修饰)中我们介绍了内存、变量、运算、const修饰 接下来我们继续学习野指针,assert断言,指针的使用和传址调用,数组名的理解和使用指针反访问数组。


提示:以下是本篇文章正文内容,下面案例可供参考

一、野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

1.1指针未初始化

我们直接看图学习:

代码语言:javascript
复制
#include <stdio.h>
int main()
{        
  int *p;//局部变量指针未初始化,默认为随机值 
  *p = 20;
  return 0;
}

当然啦,解决方法就是使其初始化,那么如何使他初始话呢? 对于明确知道指针应该指向哪里我们直接赋值;对于不知道应该指向哪里的指针,我们可以给指针赋值为NULL。(NULL 是C语言中定义的一个标识符常量,值是0,0也是地址,但是这个地址是无法使用的,读写该地址会报错。)

如图所示:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
   int num = 10;
   int*p1 = &num;
   int*p2 = NULL;
   return 0;
}

因此,指针变量不再使用时,及时置NULL,指针使用之前检查有效性

当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL。

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;
}

1.3 指针指向的空间释放

如图所示,这里我们需要避免返回局部变量的地址。

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

二、assert 断言

首先,assert()在使用使需要包含头文件assert.h 。assert用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。

代码语言:javascript
复制
assert(p != NULL);

当程序运行到这行语句时会判断表达式的真假,当表达式为真(非零)时,程序正常运行;当表达式为假(零)时,程序会报错,如下图所示:

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

assert对程序员是很友好的。它不仅能自动标识文件和出问题的行号,还有⼀种无需更改代码就能开启或关闭assert() 的机制。 如果我们在确定程序没有问题时,我们只需要在#include <assert.h> 语句的前面定义一个宏NDEBUG 。如下图所示:

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

接着,重新编译程序,编译器就会禁用文件中所有的除这条assert() 语句。 如果程序又出现问题,我们只需要移除或者注释掉#define NDEBUG

或许有人要说,if语句也可以起判断作用,但是当我们在确认代码都正确后,我们想删去if语句时,需要找到对应的代码块接着注释或者删除掉,显然是没有assert断言方便的!assert极大程度上便利了程序员。

有利就有弊了。assert() 的弊端是,因为引入了额外的检查,增加了程序的运行时间。 ⼀般我们可以在Debug 中使用,在开发环境中,在在ReRelease 版本中选禁用assert 就行在VS 这样的集成开Release 版本中,直接就是优化掉了样这在debug版本写有利于程序员排查问题,lease 版本不影响用户使用时程序的效率。

VS在这里可以切换开发环境中(相信煮包的读者中还是有小白的):

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

三、 指针的使用和传址调用

3.1 strlen的模拟实现

库函数strlen的功能是求字符串长度,长度不可能是负数,返回 size_t。统计字符串中\0 之前的字符的个数。

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

3.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;
}

运行结果如下:

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

运行结果和我们预想的完全不一样,这是为什么呢?原因如下: 实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

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

这时我们就主要借助指针来帮我们解决问题啦!

代码语言: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);
  Swap2(&a, &b);//将变量的地址传递给了函数(传址调用)
  printf("交换前:a=%d b=%d\n", a, b);
  printf("交换后:a=%d b=%d\n", a, b);
return 0;
}

传址调用,可以让函数和主调函数之间建立 真正的联系,在函数内部可以修改主调函数中的变量; 所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。 如果函数内部要修改主调函数中的变量的值,就需要传址调用

四、数组名的理解

在上一节【C语言指针一】从入门到通透:核心知识点全梳理(内存、变量、运算、const修饰)中,有这样一段代码

代码语言:javascript
复制
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

我们用 &arr[0]拿到啦数组第一个元素的地址,但是其实数组名本来就是地址,而且是数组首元素的地址

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

我们发现数组名和数组首元素的地址打印出的结果⼀模⼀样,数组名就是数组首元素(第⼀个元素)的地址。 但是凡事总是有例外的,看图展示:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  printf("%d\n", sizeof(arr));
  return 0;
}

运行结果:

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

这是为什么呢? sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

再看:

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

我们来仔细研究一下输出结果:

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

?---->40(具体详情如图所示)

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

40个字节,不就是整个数组吗?这是为什么呢?

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

这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是首元素的地址,+1就是跳过⼀个元素。 但是&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。

结论:&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)

除上述两种情况外,任何地方使用数组名,数组名都表示首元素的地址

五、使用指针访问数组

话不多说,接着看图

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

数组名arr是数组首元素的地址,可以赋值给p,其实数组名 arr 和 p 在这里是等价的。那我们可以使用arr[i]可以访问数组的元素,将 * (p+i)换成p[i]也是能够正常打印的,所以本质上p[i]是等价于*(p+i)。同理arr[i] 应该等价于 * (arr+i)

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

那么数组和指针又有怎样的关系呢?

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

这篇关于C语言指针的分享就到这里啦~


总结

从野指针的“坑”到数组与指针的联动,这些都是C语言里既基础又关键的知识点——毕竟指针用好了是“利器”,用不好就是“雷区”。 如果你在实际写代码时遇到了指针相关的问题,或者对今天讲的某部分内容有疑问,欢迎在评论区一起交流~ # 结尾 勇敢的寻宝者啊,这次旅途你挖掘到多少宝藏呢,苒苒很期待下次与您相遇!

结语:希望对寻找C语言相关内容的寻宝者有所帮助,不要忘记给博主“一键三连”哦!你的每一次鼓励都为我提供 了前行的动力!

小喵很期待与你再次寻宝奥 ᰔᩚ/•᷅•᷄\୭

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、野指针
    • 1.1指针未初始化
    • 1.2小心指针越界
    • 1.3 指针指向的空间释放
  • 二、assert 断言
  • 三、 指针的使用和传址调用
    • 3.1 strlen的模拟实现
    • 3.2 传值调用和传址调用
  • 四、数组名的理解
  • 五、使用指针访问数组
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档