C语言初阶——指针

简介: 指针是我们内存中最小单元的地址编号,因此指针能储存我们程序中各种变量在内存中的对应地址(&取出地址放入指针变量内,使用时通过*解引用访问操作即可),指针使用起来高效迅速,能够用一个指针变量记住复杂变量的地址,然后对其进行远程操作;但因为指针代表的是计算中的底层地址,不容易观察,因此很多人认为指针很难,根本学不懂,其实没那么夸张,指针不过是地址,是我们用来辅助完成程序设计的工具而已。下面让我带大家进入指针的世界!🎉🎉🎉

🦴前言


 指针是我们内存中最小单元的地址编号,因此指针能储存我们程序中各种变量在内存中的对应地址(&取出地址放入指针变量内,使用时通过*解引用访问操作即可),指针使用起来高效迅速,能够用一个指针变量记住复杂变量的地址,然后对其进行远程操作;但因为指针代表的是计算中的底层地址,不容易观察,因此很多人认为指针很难,根本学不懂,其实没那么夸张,指针不过是地址,是我们用来辅助完成程序设计的工具而已。下面让我带大家进入指针的世界!🎉🎉🎉

4476ce6bc907cf46aa4a9d2a6526c3f.png

🦴正文


📍指针与指针类型


🪡指针类型


指针跟数据一样也有自己的类型,比如整型指针、字符型指针、结构体指针等,不同的变量类型最好对应使用不同的指针类型(特殊情况除外)

06f69b388bdcbeb5d3d1c0e5d4f20b7.png


我们知道不同数据类型大小不相同,比如 int 是4字节,char 是1字节,那么对应的指针变量大小是否也是如此呢?

答案是否,指针是个很公平的东西,无论上面类型的指针,大小都是4字节(64位平台下是8字节),下面看我用 sizeof 证明这一特点:

b031cc4eae451b908ba250822124901.png



🪡指针 + - 整数


既然指针大小都是一样的,那为什么还要区分类型呢?相信这是学习指针时大多数同学的疑惑点,既然C语言是经过巧妙设计完善的,所以指针类型存在肯定有它的作用,比如下面这段代码:

1bfa8f2331e727b552bb10455f531ee.png

//指针类型-移动步长
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* pa = arr;
  char* pb = arr;
  pa++;
  pb++;
  printf("数组首元素地址:%p\n\n", &arr[0]);
  printf("整型指针+1:%p\n\n", pa);
  printf("字符型指针+1:%p\n\n", pb);
  printf("pa加1后:%d\n",*pa);
  printf("pb加1后:%d\n",*pb);
  return  0;
}

🪡不同指针类型解引用


既然不同的类型的指针移动距离不同,那么解引用权限是否也不相同呢?

c6ce6dbab3335ca9d903a24e1831dcc.png

//指针类型-解引用权限
int main()
{
  int a = 0x11223344;
  int b = 0x11223344;
  int* pa = &a;
  char* pb = &b;
  *pa = 0;
  *pb = 0;
  printf("%d %d\n", a, b);
  return 0;
}


🪡小结


指针类型有它存在的意义:


1.指针类型决定了指针移动的步长(大小是字节)

2.指针类型决定了指针解引用的权限有多大(大小同样是字节)

📍野指针


俗话说能力越大,责任越大。既然指针这么强大,那么在使用它时肯定要谨慎,避免产生野指针(指向位置不可知,即指针与变量间的强绑定关系不可控),野指针可能会内存泄露的危害(如果野指针指向某个变量,并且野指针没被销毁,那么在使用指针的过程中可能误使用到野指针,从而导致指向变量内存的改变)


🪡野指针的成因


1.指针未初始化

33543f3a549f7de9c1f710e276abc8e.png

2.指针越界访问

95a9ea58b1c62c2668094b46d22e3d7.png

3.指针指向空间被释放

642568ef028d11b5c37bd76caa8b7ca.png

🪡如何避免野指针


在使用指针时一定要小心谨慎,其次注意以下五点:

  • 1.指针在创建时最好初始化
  • 2.指针在使用时要观察是否会出现越界现象
  • 3.当指针指向的原空间被释放后,指针要及时置空(赋NULL)
  • 4.避免指针指向局部变量的地址
  • 5.指针在使用前检查有效性(类型是否匹配)


📍指针运算


指针与整数、指针间都有运算,比如前面提到了的指针 + - 整数得出不同指针类型的移动步长和解引用权限不同,其实指针 + - 整数在数组身上能起到代替下标寻找元素的作用。

🪡指针 + - 整数


3b8d6c041fb825c5b8be88d8af32f62.png

//指针 + - 整数
int main()
{
  int arr[5] = { 1,2,3,4,5 };
  int* pa = arr;
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    printf("%d ", *(pa + i));
  }
  return 0;
}

🪡指针 - 指针


指针 - 指针得到的是两个指针间的元素个数,就像日期 - 日期得到的是中间的天数一个道理。不过没有指针 + 指针这一说法,这样是没有意义的。

48d33bd54cd82dcb9ecb53712fa78a2.png


指针间的关系运算

指针在使用时需要注意一些细节,比如在使用指针遍历数组时,最好从前往后遍历,不推荐从后往前遍历,因为这是标准未定义的,盲目使用可能会造成bug。


标准规定:

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

4fda5dc2d679056c96da1bf89c2c6ef.png

//指针间的关系运算
int main()
{
  int arr[5] = { 1,2,3,4,5 };
  int* pa = arr;
  for (; pa <= &arr[4]; pa++)
  {
    printf("%d ", *pa);
  }
  return 0;
}


📍指针与数组


指针与数组间有着密不可分的关系,比如指针 + - 整数能够像数组下标一样直接访问元素,此外数组名还能作为首元素地址直接被指针指向。


数组名就是首元素地址,两种情况除外:


1.sizeof 计算数组大小时,数组名表示整个数组,计算结果即整个数组的大小(元素数量*元素大小),单位是字节

2.&数组名时,取出的是整个数组的地址,进行指针运算时,将直接跨过整个数组

ed4ebe2ae44e79dbc1f513db13039b4.png

//指针与数组
int main()
{
  int arr[5] = { 1,2,3,4,5 };
  int* pa = arr;
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    printf("arr[%d] %p<==>%p pa+%d\n", i, &arr[i], pa + i, i);
  }
  return 0;
}


📍二级指针


一级指针用来指向变量地址,二级指针则是指向一级指针的地址,二级指针也能通过解引用两次的方式远程控制变量。一颗 * 为一级指针,两颗 * 就是二级指针,依次类推可以有 n 级指针,指针指向层数越多,程序就越难理解,我们在写程序时都是一、二级指针用的多。


841d755270be6fe33b1ea24804164ea.png

//二级指针
int main()
{
  int a = 10;
  int* pa = &a;
  int** ppa = &pa;
  **ppa = 20;
  printf("初始变量a %d\n\n", a);
  printf("一级指针pa %d\n\n", *pa);
  printf("二级指针ppa %d\n\n", **ppa);
  return 0;
}

指针数组


指针数组是存储指针的数组,本质上仍是数组,不过因为数组中存储的是元素地址,因此在使用指针对其中的元素进行访问时,需要使用二级指针。

3c9c69081acbb87e46f9e917ccc15d4.png

//指针数组
int main()
{
  int* arr[5] = { 0 };
  int** pa = arr;
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    arr[i] = &i;
    printf("%d ", **pa);
  }
  return 0;
}

🦴总结


 从指针是什么到指针数组的,指针初阶的内容至此我们就介绍完了。现在我们可以开始愉快的使用指针变量了,记住不要造出野指针,也不要让指针的指向关系过于复杂,避免我们在学习指针的过程中出现从入门到放弃的情况。


 如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正!


8cddb3055abb722ea8fa21aeb4ae774.png


目录
相关文章
|
2月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
124 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
2月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
182 9
|
2月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
64 7
|
3月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
270 13
|
3月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
3月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
221 3
|
3月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
3月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
72 1
|
3月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
3月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。