当前位置: 首页 > news >正文

【C语言初阶】C语言指针全攻略:解锁C语言深层奥秘的钥匙

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C语言 “ 登神长阶 ”
🤡往期回顾🤡:C语言操作符
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

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

❀指针

  • 📒1. 指针和指针类型
    • 🌄指针的概念
    • 🏞️指针 + - 整数
    • ⛰️指针的解引用
  • 📙2. 野指针
    • 🎈野指针成因
    • 🎩如何规避野指针
  • 📕3. 指针运算
    • 🍁指针 - 指针
    • 🍂指针的关系运算
  • 📚4. 指针和数组
  • 📜5. 二级指针
  • 📝6. 指针数组
  • 📖7. 总结


前言:在编程的广阔天地里,C语言以其接近硬件的特性和强大的灵活性,始终占据着举足轻重的地位。而在这片由代码构建的领域中,指针无疑是那把开启C语言深层奥秘的钥匙。它不仅让程序员能够直接访问和操作内存,更是构建复杂数据结构、实现高效算法、进行底层开发不可或缺的工具

对于许多初学者而言,指针或许是一个令人望而生畏的概念。那些看似晦涩难懂的语法规则和抽象的概念,常常让人望而却步。然而,正如攀登高峰的过程虽然艰难,但一旦站在山顶,便能领略到前所未有的壮丽景色。学习C语言指针,也是一场从迷茫到清晰、从畏惧到掌握的旅程

在这篇文章中,我们将一起踏上这段旅程,从指针的基础概念讲起,逐步深入到内存管理、数组操作、函数参数传递等核心话题。我们将通过生动的示例、清晰的解释和实用的技巧,帮助你克服对指针的恐惧,逐步建立起对指针的深刻理解和灵活运用能力

让我们一起,用指针这把钥匙,打开C语言世界的大门,探索其中的无限可能和精彩世界!


📒1. 指针和指针类型

🌄指针的概念

指针理解的2个要点:

  • 指针是内存中一个最小单元的编号,也就是地址
  • 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址


在这里插入图片描述
指针变量

我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量

代码示例 (C语言):

int main()
{int a = 10; // 在内存中开辟一块空间int* p = &a; // 这里我们对变量a,取出它的地址,可以使用&操作符。// a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量// 中,p就是一个之指针变量return 0;
}

指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)


对于32位的机器,假设有32根地址线

那么32根地址线产生的地址就会是:

//00000000 00000000 00000000 00000000
//00000000 00000000 00000000 00000001
...
//11111111 11111111 11111111 11111111

这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给
(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB == 2^32/1024/1024/1024GB == 4GB)
4G的空间进行编址

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址

总结:

  • 指针变量是用来存放地址的,地址是唯一标示一个内存单元的
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节

🏞️指针 + - 整数

代码示例 (C语言):

int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);printf("%p\n", pi);printf("%p\n", pi + 1);return 0;
}

指针的类型决定了指针向前或者向后走一步有多大(距离)


⛰️指针的解引用

代码示例 (C语言):

int main()
{int n = 0x11223344;char* pc = (char*)&n;int* pi = &n;*pc = 0; //重点在调试的过程中观察内存的变化。*pi = 0; //重点在调试的过程中观察内存的变化。return 0;
}

在这里插入图片描述

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节


📙2. 野指针

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


🎈野指针成因

指针未初始化

int main()
{int *p; // 局部变量指针未初始化,默认为随机值*p = 20;return 0;
}

指针越界访问

int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i <= 11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}

指针指向的空间释放

(动态内存开辟的时候会提及… …)


🎩如何规避野指针

  • 指针初始化
  • 小心指针越界
  • 指针指向空间释放,及时置NULL
  • 避免返回局部变量的地址
  • 指针使用之前检查有效性
int main()
{int* p = NULL;//....int a = 10;p = &a;if (p != NULL){*p = 20;}return 0;
}

📕3. 指针运算

🍁指针 - 指针

指针 - 指针表示两个指针相隔的距离

int main()
{int a = 10;int* p1 = &a;int* p2 = p1;p1++;printf("%d", p1 - p2);return 0;
}

🍂指针的关系运算

指针之间不能直接使用 <、>、<=、>= 进行比较,除非这些指针:

  • 指向同一个数组的元素
  • 指向同一个结构体(或联合体)的不同成员

对于指向不同数组或不同对象的指针,进行关系运算的结果是未定义的

标准规定:

  • 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较

📚4. 指针和数组

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

在这里插入图片描述
结论:数组名表示的是数组首元素的地址

// 因此我们这么写也是可以的
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址

我们可以通过数组名来访问数组元素

int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,0 };int* p = arr; //指针存放数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);}return 0;
}

在这里插入图片描述
p+i 其实计算的是数组 arr 下标为i的地址


我们也就可以直接通过指针来访问数组

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };int* p = arr; //指针存放数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

📜5. 二级指针

二级指针储存指针变量的变量

在这里插入图片描述

关于二级指针:

  • *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , ppa 其实访问的就是 pa
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

📝6. 指针数组

指针数组储存指针的数组

int arr1[5];
char arr2[6];

在这里插入图片描述

int* arr3[5];

arr3是一个数组,有五个元素,每个元素是一个整形指针
在这里插入图片描述


📖7. 总结

在探索C语言指针的旅途中,我们不难发现,指针不仅是C语言强大功能的核心,也是理解底层内存管理和高级编程技巧的关键。通过深入学习和实践,我们逐渐揭开了指针神秘的面纱,掌握了它们如何像桥梁一样连接起变量、数组、结构体乃至更复杂的数据结构

在本文的结尾,我想强调的是,掌握C语言指针并非一蹴而就的过程,它需要时间的积累、实践的锤炼以及对错误和困惑的不懈探索。每一次对指针的深入理解,都是对C语言编程能力的一次飞跃,也是对自己逻辑思维和问题解决能力的一次提升

同时,我们也要认识到,指针虽然强大,但使用时必须格外小心。错误的指针操作可能导致内存泄露、野指针、段错误等一系列问题,严重影响程序的稳定性和安全性。因此,在享受指针带来的便利和效率的同时,我们也必须时刻铭记其潜在的风险,并养成良好的编程习惯,如初始化指针、检查空指针、合理使用指针类型等

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Springboot集成Mybatis在不同文件夹下出现同名文件时启动报错
  • Java实现pdf/word文字识别,调用OCR提取图片文字聚合
  • 厦门商家微信小程序、抖音、支付宝小程序同步上线
  • C语言宏中“#”和“##”的用法
  • 优先级队列的实现
  • 【uniapp】vue3+vite配置tailwindcss
  • 力扣热题100_链表_234_回文链表
  • ubuntu设置共享文件夹,非虚拟机,服务器版
  • XSS DOM漏洞复现 与DOM 破坏
  • ARM/Linux嵌入式面经(二四):国光电器
  • 雷达气象学(9)——反射率因子图分析(强对流篇)
  • 二十、观察者模式
  • 在postman设置请求里带动态token,看看这两种方法!
  • Python接口自动化之unittest单元测试
  • 深入理解指针(五)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • MYSQL 的 IF 函数
  • react 代码优化(一) ——事件处理
  • SpriteKit 技巧之添加背景图片
  • Terraform入门 - 1. 安装Terraform
  • TypeScript实现数据结构(一)栈,队列,链表
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 深入 Nginx 之配置篇
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 思维导图—你不知道的JavaScript中卷
  • 一个JAVA程序员成长之路分享
  • 用Visual Studio开发以太坊智能合约
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • 阿里云ACE认证学习知识点梳理
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • (11)MATLAB PCA+SVM 人脸识别
  • (20050108)又读《平凡的世界》
  • (C语言)逆序输出字符串
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (编译到47%失败)to be deleted
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (强烈推荐)移动端音视频从零到上手(下)
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .NET和.COM和.CN域名区别
  • /tmp目录下出现system-private文件夹解决方法
  • ::前边啥也没有
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @Query中countQuery的介绍
  • @RestController注解的使用
  • [《百万宝贝》观后]To be or not to be?