【C语言】指针和数组的深入理解(第一期)(上)

简介: 在开启这个章节之前,我看过很多书上都是先讲数组,后续又说数组名就是是地址,如果没有前期知识的铺垫,大多数都会想,地址是个啥玩意?到底什么是数组?什么是指针?他们之间又有什么样的关系?本章节我们将深入学习。

在开启这个章节之前,我看过很多书上都是先讲数组,后续又说数组名就是是地址,如果没有前期知识的铺垫,大多数都会想,地址是个啥玩意?到底什么是数组?什么是指针?他们之间又有什么样的关系?本章节我们将深入学习。

1、指针是什么?

在看这个问题之前,我们先引入一段简单的代码做铺垫:

int main()
{
  int a = 10; //定义a变量开辟了一块空间并且初始化
  a = 20; //使用的是a的空间:左值
  int b = a; //使用的是a的内容:右值,此时a等价于20
  return 0;
}

同过上面代码及注释,我们可以看出,同样一个 a 变量,在不同的应用场景中,a 的本身含义是不同的!

那么这个代码跟指针又有什么关系呢?

指针就是地址!就好比你 &a,取出变量 a 的地址,而这个就可以称作是指针!地址的本质上是数据!数据是可以被保存在变量空间里面的!

这里可能有人会说,你在胡说,那我之前就知道指针是存放地址的,你现在又告诉我指针就是地址?这直接把我搞糊涂了。这里你就不够严谨了!我们口头上说的指针,指的是指针变量!什么是指针变量?就是保存指针的变量,也可以说是保存地址的变量,这个变量就叫做指针变量!

从严格意义上,指针和指针变量是不同的,指针就是地址,而指针变量是C语言中的变量!要在特定区域开辟空间,用来保存地址数据,也可以被取出地址。

但是我们在口语化表达的时候,经常将这两个概念混合,是以前的书不够严谨?还是教学的人学的不够通透,或者是翻译外国资料的时候没翻译清楚?具体原因无从考证,在以后,我们分开理解,和别人讨论时,要明确概念,指针就是指针,指针变量就是指针变量!

有了上面的认识,我们再来看一段代码:

int main()
{
  int a = 10;
  int* p = &a; //取出变量a的地址放到指针变量p中
  p = (int*)0x11223344; //使用p变量的空间,左值
  int* q = p; //使用p变量的内容:右值,此时的p等价于0x11223344
  return 0;
}

我们再次总结一下:指针就是地址!指针变量本质是变量,里面保存的是地址(指针)值!

2、为什么要有指针?

这里我们举个例子来说明,张三在寝室打游戏,到了中午肚子饿了,于是拿起手机点了份外卖,外卖小哥送到寝室楼下,一头雾水(假设没有门牌号的情况下),打电话问张三:“你在哪里啊?”,张三:“我在宿舍里啊”,“你在哪个宿舍啊?”,“我不知道该怎么表达,你一间间找吧”,外卖小哥很无奈,只能一间间的找,好不容易找到对应的房间,菜都凉了,外卖小哥成功获得了一个差评。

通过上述案例,证明了门牌号的重要性,显然而知没有门牌号是多么麻烦的一件事,门牌号归根到底就是为了提高查找效率!

那么我们类比到计算机中,CPU 在内存中寻址的基本单位是字节,在32位机器下,最多能识别 4G 的物理内存,既然 CPU 寻址是按照字节,但是内存又很大,所以可以看作众多字节的集合:

其实我们门牌号就可以等价于每个字节空间对应的地址,也就是该空间对应的指针

存在指针的意义就是为了提高CPU寻址效率。

那么如何理解编址呢?

我们得了解一个概念,计算机内部是有很多硬件单元的,而硬件单元是需要互相协同工作,最基本的他们之间要互相进行信息传递,而我们一个个硬件之间是相互独立的,总得想个办法把他们相关联起来,就举例CPU和内存之间要传递大量的数据,那么他们之间就会用“线”给连起来,今天我们只关心地址总线。

在CPU需要访问内存的某一个空间的时候,必须知道这个字节空间在内存的什么位置上,因为内存中字节很多,所以就如我们上面所说引入了地址,也就是给内存进行编址,在计算机中的编址,并不是把每个字节的地址信息保存下来,而是通过各硬件厂商出厂前就设计完成了。

硬件编址也是如此,32 位机器有 32 根地址总线,每根线可以表示两种形态 0,1【高电压低电压】,也就是说 1 根线能表示 2 种含义,2 根线表示 4 种含义,以此类推,32根线能表示 2^32 种含义,而每种含义则代表着一个地址,在地址信息被下达给内存,在内存的内部就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。(点到即可)

指针的大小?

有了上面的认识就理解指针的大小就很简单了,如果是 32 位机器我们要存一个地址数据是不是应该用 4 个字节,那如果是 64 位的环境就应该用 8 个字节大小空间来存放一个地址,那么我们的结论正不正确呢?

结论:指针变量是用来存地址的,地址的大小表示是根据操作环境变化的,所以要想放得下地址的数据大小,指针的变量大小也会随着系统环境而变化。

3、指针的内存布局

在很多教材上都会说某某指针指向了某某变量,是真的指向吗?既然指针变量是用来存地址的,那一个 int 占四个字节,他存的又是哪个字节的地址呢?下面我们来一一讨论。

先看一段代码:

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

这里我们先来分析一下,创建了一个整型 a 变量,同时初始化为 10,接着我们创建了一个整型指针变量 p ,并取出变量 a 的地址赋值给指针变量 p,那么他们的内存布局是这样的:

所以我们常说的指针变量 p 指向变量 a 实质上是指针变量 p 中存放着 a 变量的起始地址,也是最小的地址。也能明白,取地址本质是取出变量的起始地址。如果有的老师画图把指向图画在变量中间的话,那么他显然画图画的很不严谨!

相关文章
|
5天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
16 3
|
17天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
42 0
|
4天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
12 2
|
13天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
23 1
|
16天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
12 2
|
16天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
16天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
23天前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
22天前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
22天前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。