爱上C语言:指针很难?来来来,看看这篇(基础篇)

简介: 爱上C语言:指针很难?来来来,看看这篇(基础篇)

🚀前言

大家好啊😉!今天阿辉将为大家介绍C语言的指针部分,包括认识指针、指针变量、指针运算、const关键字、assert断言以及二级指针,关注阿辉不迷路哦 😘 ,内容干货满满😋,接下来就跟着阿辉一起学习吧👊

🚀认识指针

在讲指针前,我们先了解一下内存

我们在购买电脑前都会很在意电脑的内存大小

内存是计算机存储数据的一种方式,它可以存储正在运行的程序和数据

如果内存容量太小,会导致计算机不能同时运行多个程序或运行大型应用程序时速度变慢。因此,在一定程度上,内存越大可以提高计算机的性能和运行速度

现在电脑普遍内存大小为4GB、8GB和16GB几种,这么大的内存,我们怎么样精确的找到我们想要的数据呢,这时智慧的科学家就把每个字节(byte也就是8个bit位)大小的空间都编上号,通过编号来精确定位,而这个编号就是指针也就是地址

  • 我们习惯上不说指针而是地址,通常口头上说的指针指的是指针变量

针指变量又是什么?别慌我们接着看👊

🚀指针变量

既然是变量,那它就也有类型,只不过指针变量存的是地址罢了

✈️指针变量的创建

type * p;
p是指针变量的名字
这里的type指的是所指向的数据类型是type
type可以是int、char、float、double、int[] ....
而这颗 * 表示p是指针
type*这俩一起表示p的类型

不管什么类型的指针,指针都是用来存放地址的,而指针变量开辟空间的大小也取决于地址

  • 32位平台下地址是32个bit位,指针变量大小是4个字节
  • 64位平台下地址是64个bit位,指针变量大小是8个字节
  • 注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的

那么指针的类型有什么用呢,别急我们接着看👇

✈️取地址操作符(&)

🌰栗子

int main()
{
  int a = 10;
  &a;
  return 0 ;
}

取地址操作符(&) 取出的是数据存储的在低地址位置的地址,因为很多数据并非一个字节,如short、int、doubt等并不需要取出每个字节的地址

上述代码中&a的地址就是0x00BFFC44

✈️解引用操作符(*)

我们知道指针是用来存储地址的,那指针到底有什么用呢?

其实指针可以通过解引用操作,由其存储的地址找到所指向的内存空间,从而改变该块空间的内容

🌰栗子

上述例子中取出a的地址赋给指针p,然后通过对p解引用操作找到a*p就等价于a*p = 1也就是a = 1所以打印出来的a的值为1

解引用操作与取地址操作互为反操作

🚀指针运算

✈️指针变量加减整数

跟字面意思一样,就是指针变量加上或者减去一个整数

ptr + n;
ptr - n;
ptr是指针变量,n为整数

那它有什么用呢?这里就与指针的类型有关了,指针的类型决定了指针加减整数跳过的空间大小

🌰栗子

int main()
{
  int a = 0;
  char c = 0;
  double d = 0.0;
  int* pa = &a;
  char* pc = &c;
  double* pd = &d;
  printf("pa = %p\n", pa);
  printf("pa + 1= %p\n", pa + 1);
  printf("pc = %p\n", pc);
  printf("pc + 1= %p\n", pc + 1);
  printf("pd = %p\n", pd);
  printf("pd + 1= %p\n", pd + 1);
  return 0;
}

上述图中我们可以看到,int*类型指针跳过4个字节,char*类型指针跳过一个字节而double*类型指针跳过8个字节(加减整数就是跳过该整数倍类型的空间大小,比如int*就是跳过8个字节)

结论:指针的类型决定了指针向前或向后移动一步的距离

✈️指针减指针

指针减指针得到的是两个指针之间的元素个数

🌰栗子

切记指针与指针相减俩指针必须是同一块空间

✈️指针间关系运算

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

✈️野指针

我们创建变量都会去给变量初始化,否则变量里面存的就是随机值,而如果指针创建没有初始化会造成野指针,野指针会导致程序崩溃或者产生难以调试的bug

导致野指针的几种情况

  • 指针未初始化
  • 指针越界或者非法访问
  • 指针所指向的空间已释放

🌰栗子

#include <stdio.h>
int main()
{
  //指针未初始化
  int* p1;//指针未初始化
  *p1 = 5;//在不知道指针指向的空间在哪时,强行改变指向空间的地址
  //指针越界访问
  int arr[5] = { 1,2,3,4,5 };
  int i = 0;
  int *p2 = arr;//p指向数组的首地址
  for (i = 0; i <= 10; i++)
  {
    printf("%d\n", *(p2++));
  }
  //当p2指向的空间超过数组的最后一个元素时,指针就非法访问,即越界访问了未知空间
  //指向的空间被释放了
  int j = 0;
  for (j = 0; j < 5; j++)
  {
    int a = 5;
    a += 1;
  }
  int* p3 = &a;//当循环结束之后,局部变量a向内存申请的空间就被释放了。
  //此时p3指针指向的空间就是已经被释放掉的空间
  return 0;
}

我们可以通过以下几种方式来规避野指针

  1. 指针初始化时明确指向的内容,不明确时设置为空指针(NULL)
  2. 小心指针越界:使用指针是,仔细检查,指针是否会有越界的情况发生
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

🚀const关键字

当变量被const修饰后,该变量就具有了常属性,本质依然是变量但是不能被修改

🌰栗子

如上图,在vs2022编译器上变量被const修饰后,如果变量在赋值操作符左边会直接报错

我们来看看一种神奇的操作👇

a虽然被const修饰了,但是照样被改了

我们发现我们不能直接改a的值,但是我们可以通过指针间接改掉a的值

我们也可以给指针也用const修饰,但是const修饰指针复杂了一丢丢,我直接上代码👊

int main()
{
  const int a = 0,b = 0,c = 0;
  下面这两种const都在*左边表示不能通过解引用操作更改所指向空间的内容
  但是可以把p1和p2内存的地址给改了
  const int* p1 = &a;
  int const* p2 = &b;
  下面这种表示const在*右边表示不能更改p3的值,就是不能把p3内存的地址给改了
  但是可以通过解引用操作更改c的值
  int* const p3 = &c;
  return 0;
}

结论:const修饰指针变量的时候

  • const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变
    但是指针变量本身的内容可变。
  • const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变

🚀assert断言

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

例如:

assert(p != NULL);

上面代码在程序运行到这一行语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运行,否则就会终止运行,并且给出报错信息提示

assert()的好处:

  • 能自动标识文件和出问题的行号
  • 如果已经确认程序没有问题,不需要再做断⾔,就在 #include <assert.h> 语句的前面,定义⼀个宏 NDEBUG

例如:

#define NDEBUG
#include <assert.h>

assert() 是一种在程序中插入的断言,用于在运行时检查特定条件是否为真。其缺点之一是引入额外的检查,可能会增加程序的运行时间。在开发过程中,可以在调试版本中使用 assert() 来帮助程序员排查问题,而在发布版本中禁用 assert() 来提高程序的效率。在一些集成开发环境中,如Visual Studio,发布版本中通常会自动优化掉 assert()。因此,使用 assert() 需要在调试和发布版本之间进行适当的管理,以兼顾程序员的开发需求和最终用户的使用效率。

🚀二级指针

二级指针就是存储指针变量地址的指针

int a = 1![请添加图片描述](https://ucc.alicdn.com/images/user-upload-01/d52ded127def45478ddafeabedd09966.png)
0;
int * p = &a;
int* * pp = &p;
两颗*指的是pp是一个二级指针
左边的int*是pp所指向的变量的类型是int*
int* *是pp的类型
*(pp)等价于p
*(*(pp))等价于a

图解

不仅有二级指针还有三级、四级、五级指针等等,不过一般最多用到三级指针,更高的用不上


到这里,阿辉今天关于指针的基础知识分享就结束了,不过这一篇仅仅是深入指针的基础,希望这篇博客能让大家有所收获, 如果觉得阿辉写得不错的话,记得给个赞呗,你们的支持是我创作的最大动力🌹

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

热门文章

最新文章