编程语言爱好者C语言学习数据结构与算法教程

C语言指针是什么?学好了指针就几乎学好了C语言……指针注意事项

2019-09-24  本文已影响0人  榆西带你飞

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

在C语言的学习过程中,指针是相当重要的一部分,学好指针对C语言的学习有很重要的意义。

想学好C语言的伙伴可以私信回复 领取资料 领取C语言学习资料哦!

image

指针和内存

如果说内存相当于街道,那么地址就相当于位于街道中房子的房号。

一个字节(8bit)包含一个地址。内存中每一个位置都包含一个独一无二的地址标识,而每一个位置都包含一个值。

注意:①区别指针变量的内容(即地址)、指针指向变量的内容、指针的地址。

为什么程序中的数据会有自己的地址?

弄清这个问题我们需要从操作系统的角度去认知内存。

电脑维修师傅眼中的内存是这样的:内存在物理上是由一组DRAM芯片组成的。

image

而作为一个程序员,我们不需要了解内存的物理结构,操作系统将RAM等硬件和软件结合起来,给程序员提供的一种对内存使用的抽象。,这种抽象机制使得程序使用的是虚拟存储器,而不是直接操作和使用真实存在的物理存储器。所有的虚拟地址形成的集合就是虚拟地址空间。

image

在程序员眼中的内存应该是下面这样的。

image

也就是说,内存是一个很大的,线性的字节数组(平坦寻址)。每一个字节都是固定的大小,由8个二进制位组成。最关键的是,每一个字节都有一个唯一的编号,编号从0开始,一直到最后一个字节。如上图中,这是一个256M的内存,他一共有256x1024x1024 = 268435456个字节,那么它的地址范围就是 0 ~268435455 。

由于内存中的每一个字节都有一个唯一的编号,因此,在程序中使用的变量,常量,甚至数函数等数据,当他们被载入到内存中后,都有自己唯一的一个编号,这个编号就是这个数据的地址。指针就是这样形成的。

下面用代码说明

<pre class="ql-align-justify">#include <stdio.h>int main(void)
{
char ch = 'a';
int num = 97;
printf("ch 的地址:%p\n",&ch); //ch 的地址:0028FF47
printf("num的地址:%p\n",&num); //num的地址:0028FF40
return 0;
}</stdio.h> </pre>

image

指针的值实质是内存单元(即字节)的编号,所以指针 单独从数值上看,也是整数,他们一般用16进制表示。指针的值(虚拟地址值)使用一个机器字的大小来存储,也就是说,对于一个机器字为w位的电脑而言,它的虚拟地址空间是0~2w - 1 ,程序最多能访问2w个字节。这就是为什么xp这种32位系统最大支持4GB内存的原因了。

我们可以大致画出变量ch和num在内存模型中的存储。(假设 char占1个字节,int占4字节)

image

变量和内存

为了简单起见,这里就用上面例子中的 int num = 97 这个局部变量来分析变量在内存中的存储模型。

image

已知:num的类型是int,占用了4个字节的内存空间,其值是97,地址是0028FF40。我们从以下几个方面去分析。

1、内存的数据

内存的数据就是变量的值对应的二进制,一切都是二进制。97的二进制是 : 00000000 00000000 00000000 0110000 , 但使用的小端模式存储时,低位数据存放在低地址,所以图中画的时候是倒过来的。

2、内存数据的类型

内存的数据类型决定了这个数据占用的字节数,以及计算机将如何解释这些字节。num的类型是int,因此将被解释为 一个整数。

3、内存数据的名称

内存的名称就是变量名。实质上,内存数据都是以地址来标识的,根本没有内存的名称这个说法,这只是高级语言提供的抽象机制 ,方便我们操作内存数据。而且在C语言中,并不是所有的内存数据都有名称,例如使用malloc申请的堆内存就没有。

4、内存数据的地址

如果一个类型占用的字节数大于1,则其变量的地址就是地址值最小的那个字节的地址。因此num的地址是 0028FF40。 内存的地址用于标识这个内存块。

5、内存数据的生命周期

num是main函数中的局部变量,因此当main函数被启动时,它被分配于栈内存上,当main执行结束时,消亡。

如果一个数据一直占用着他的内存,那么我们就说他是“活着的”,如果他占用的内存被回收了,则这个数据就“消亡了”。C语言中的程序数据会按照他们定义的位置,数据的种类,修饰的关键字等因素,决定他们的生命周期特性。实质上我们程序使用的内存会被逻辑上划分为: 栈区,堆区,静态数据区,方法区。不同的区域的数据有不同的生命周期。

无论以后计算机硬件如何发展,内存容量都是有限的,因此清楚理解程序中每一个程序数据的生命周期是非常重要的。

我会在以后的文章中再对C语言的内存管理做出介绍,敬请期待。

下面说一下C语言关于指针的注意事项

一、指针的四个关键概念

1、指针的类型

2、指针指向的类型

3、指针的值,也就是指针指向的地址

4、指针自己所占用的内存空间

注意:指针变量所存的内容就是内存的地址编号!

例如:

int **pp = NULL;

1、指针的类型是 int **

2、指针指向的类型 int *

3、指针的值为NULL

4、指针自己所站内存的大小 sizeof(pp)

二、强制类型转换

强制类型不会改变内存中二进制的排列与顺序,只会将二进制按照目标类型解释。

例如:

int i = 10;

二进制位 0110;

float f = (float)i;

变量f的二进制还是 0110

三、关于CONST修饰变量

看const修饰变量的时候,完全可以将数据类型名视而不见

例如

int const p; //修饰p , p可以变 , *p不能够变

const int p; //修饰p , p可以变 , *p不能够变

const int * const p; //修饰p和*p , p不可变, *p也不可变

四、关于变量类型

1、数据类型的本质是固定内存空间大小的别名

2、变量的本质是一段连续内存空间起始地址的别名

五、关于变量声明的意义

1、建立变量符号表

通过声明变量,编译器可以建立变量符号表,如此一来,程序中用到了多少变量,每个变量的类型是什么,编译器非常清楚,是否使用了没有声明的变量,编译器在编译期间就可以发现。从而帮助开发人员远离由于疏忽而将变量名写错的情况

2、变量的数据类型指示系统为变量分配多少内存空间

3、变量的数据类型指示了系统如何解释存储空间中的值,同样的数值,不同的类型将有不同的解释。int占据4个字节,float也占据4个字节,在内存中同样是存储的二进制数,并且这个二进制数也没有标志区分当前是int型还是float型。如何区分?就是通过变量的数据类型来区分。由于声明建立了变量符号表,所以系统知道变量该如何解释

4、变量的数据类型确定了该变量的取值范围

例如短整型数据取值-32767~32767之间

5、不同的数据类型有不同的操作

如整数可以求余。C语言用符号”%”表示求余。整数可以,实数不可

六、关于函数调用传递指针的总结

1、如果在被调函数中想修改主调函数中变量的值,则需要将主调函数中的变量的地址(指针)传递到被调函数中

2、如果主调函数要传递一个超大的数据到被调函数中时,也可以将主调函数的变量的地址(指针)传递到被调函数中,这样有利于提高程序的性能

3、传递N级指针是为了修改N-1级指针的值,

例如 : int *p;如果需要修改p的值,则需要将&p传递到函数中,才能修改p的值

七、关于指针与数组互换的问题

在表达式中,指针和数组是可以互换的,因为他们在编译器里面的最终形式都是指针,并且都可以进行取下标操作

八、数组与指针的区别

数组: 一个数组就是一个地址,并且该地址是一个常量值,不能改变。因此,数组名不能作为左值。

指针: 一个指针就是一个地址的地址 , 并且该地址是可以改变的。也就可以作为左值。

九、指针与地址的区别

指针就是地址,但地址并不是指针。指针有类型,地址没有类型。

十、传值与传址的区别

c语言其实只有一种传值方式,就是按值传递

按值、按址传递的区别在于,在函数中使用的方式而已。如果在函数中是要修改地址所指向内存的值,则可以修改实参地址指向的内容。因为在内存中内存地址是唯一的,所以不论在什么地方,都可以通过地址来修改内存中的值

指针变量总结

image

想学好C语言的伙伴可以私信回复 领取资料 领取C语言学习资料哦!
【C/C++学习、面试;文档、视频资源免费获取​】

12.png
上一篇 下一篇

猜你喜欢

热点阅读