指针(Pointer)就是内存的地址,C语言允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组、函数以及其他指针变量的地址。
程序在运行过程中需要的是数据和指令的地址,变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符:在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名和数组名表示的是代码块或数据块的首地址;程序被编译和链接后,这些名字都会消失,取而代之的是它们对应的地址。
1.NULL指针
1).NULL指针是一个特殊的指针变量,表示不指向任何东西,它可以赋值给一个指针,用于表示那个指针不指向任何值。对一个NULL指针进行解引用操作是非法的,引起的后果因编译器而异,两个常见的后果分别是返回内存位置零的值和终止程序。
2).在使用指针变量之前,需要对其显示的初始化,如果知道指针将被初始化为什么地址,就将他初初始化为什么地址,否则将其初始化为NULL。在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
3).通过ptr == NULL,检查一个指针是否为空。
2.指针变量作为函数参数
数组是一系列数据的集合,C语言不允许直接传递数组的所有元素到函数内部,如果希望在函数内部操作数组,必须传递数组指针。
参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是指将一块内存上的数据复制到另一块内存上。对于像 int、float、char 等基本类型的数据,它们占用的内存往往只有几个字节,对它们进行内存拷贝非常快速。而数组是一系列数据的集合,数据的数量没有限制,可能很少,也可能成千上万,对它们进行内存拷贝有可能是一个漫长的过程,会严重拖慢程序的效率,为了防止技艺不佳的程序员写出低效的代码,C语言没有从语法上支持数据集合的直接赋值。
3.指针作为函数返回值
C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数。
不能将一个栈变量地址通过函数返回值返回;栈变量包括函数内部定义的局部变量和函数参数。具体原因涉及到C语言内存管理机制,栈变量在函数运行结束后会由系统自行收回,此时返回栈变量的地址可能会指向一个意想不到的值,该情况一般不会引起程序报错,实际中比较难定位问题,因此使用时要细心;
可以通过函数返回堆地址,堆地址要用free函数手动释放申请的内存,不然有可能发生内存泄露;当动态分配的内存不在使用时, 它应给被释放,这样以后可以重新使用内存。分配内存但是在使用完毕之后不进行释放将会引起内存泄露。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
可以通过函数返回值返回静态区/全局区变量地址,因为在程序运行过程中静态区/全局区变量是一直存在的。
4.函数指针
函数和数组一样,都是占用内存中一段连续区域,数组名、字符串名、函数名在使用时都会指向占用内存区域的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。