一、为什么学指针
指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作。另外,通过指针可以更便捷地操作数组。在一定意义上可以说,指针是 C 语言的精髓。没有学指针就相当于还没有学C语言(夸张!)
二、指针是什么
认识指针之前先了解内存与地址
在计算机中,数据是存放在内存单元中的,一般把内存中的一个字节称为一个内存单元。为了更方便地访问这些内存单元,可预先给内存中的所有内存单元进行编号,这些编号叫做地址,通过地址可准确找到其对应的内存单元。由于每一个地址编号均对应一个内存单元,因此可以形象地说一个地址编号就指向一个内存单元。
所以C 语言中把地址形象地称作指针
三、指针实战
1、指针变量的定义
可以保存地址值(指针)的变量称为指针变量。
用法:类型 * 变量名;(*号标识该变量为指针类型)
代码展现:
#include<stdio.h>
int main()
{
int a=3;//定义整型变量a的值是3
int *p;//定义整型的指针变量是p
p = &a;//将a的地址给p
//*p=&a; //错误,指针变量是p而不是*p
printf("存放a的地址:%x\n",&a);
printf("存放a的地址:%x\n",p);
return 0;
}
*p=&a; //错误,指针变量是p而不是*p
&:取这个变量的地址,地址是十六进制的形式(%x)。
如scanf("%d",&a);把一个数存在内存中,该内存的地址取名位a。
指针变量中只能保存相同类型变量的地址
int a=3,b;
char c;//定义整型变量a的值是3
int *pa,*pb;
float *pc;//定义整型的指针变量是p
pa=&a;//正确。pa基类型为int,a为int型变量,类型一致
pb=&c;//错误。pb基类型为int,c为char型变量,类型不一致
pc=&c;//正确。pc基类型为char,c为char型变量,类型一致
*pa=&a;//错误。指针变量是pa而非*pa
2、指针变量的引用
作用:访问内存空间,一般分为直接访问和间接访问。
如果知道内存空间的名字,可通过名字访问该空间,称为直接访问。由于变量即代表有名字的内存单元,故通过变量名操作变量,也就是通过名字直接访问该变量对应的内存单元。
如:int a=3; printf("%d",a)。即为:通过名字访问该空间(内存)
如果知道内存空间的地址,也可以通过该地址间接访问该空间。对内存空间的访问操作一般指的是存、取操作,即向内存空间中存入数据和从内存空间中读取数据。
代码解释:
#include<stdio.h>
int main()
{
int a=888,b;
char c;//定义整型变量a的值是3
int *pa=&a,*pb;
float *pc;//定义整型的指针变量是p
printf("直接访问:a=%d\n",a);//直接访问形式,就是直接通过原来的变量名来进行访问
//间接的访问形式,就是间接通过指针指向的变量,来间接的访问原来变量的,此处的*为指向的意思
printf("间接访问:a=%d\n",*pa);
//直接访问修改变量
a=666;
printf("直接访问:a=%d\n",a);
//间接访问修改,注意:*在除了指针初始化是指针运算符,在其他的时候为指向的意思
*pa = 999;//*来访问指针所指向的空间 ,将该空间(a)的值改成999
printf("间接访问(值):a=%d\n",*pa);
printf("间接访问(地址):a=%x\n",pa);
return 0;
}
注意:*在除了指针初始化是指针运算符,在其他的时候为指向的意思
3、“野”指针
定义:把没有合法指向的指针称为“野”指针。因为“野”指针随机指向一块空间,该空间中存储的可能是其他程序的数据甚至是系统数据,故不能对“野”指针所指向的空间进行存取操作,否则轻者会引起程序崩溃,严重的可能导致整个系统崩溃。
代码解释:
#include<stdio.h>
int main()
{
int *pi,a; //pi未初始化,无合法指向,为“野”指针
*pi=3;
a=*pi;
return 0;
}
pi指向的内存空间不明确 *pi=3; //运行时错误!不能对”野”指针指向的空间做存入操作。该语句试图把 3 存入“野”指针pi所指的随机空间中,会产生运行时错误。 a=*pi; //运行时错误!不能对”野”指针指向的空间取操作。该语句试图从“野”指针pi所指的空间中取出数据,然后赋给变量a同样会产生运行时错误。
正确的使用方法:
#include<stdio.h>
int main()
{
int *pi,a; //pi未初始化,无合法指向,为“野”指针
pi=&a;//让pi有合法的指向,pi指向a变量对应的空间
*pi=3;//把3间接存入pi所指向的变量a对应的空间
printf("%d\n",*pi);
printf("%d\n",a);
return 0;
}
实在不知道指针所指向的地址该怎么办?
4、C 中的 NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
当上面的代码被编译和执
行时,它会产生下列结果:
ptr 的地址是 0x0
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。
5、指针与数组
数组是一系列相同类型变量的集合,不管是一维数组还是多维数组其存储结构都是顺序存储形式,即数组中的元素是按一定顺序依次存放在内存中的一块连续的内存空间中(地址连续)。
指针变量类似于一个地址箱,让其初始化为某个数组元素的地址,以该地址值为基准,通过向前或向后改变地址箱中的地址值,即可让该指针变量指向不同的数组元素,从而达到通过指针变量便可以方便地访问数组中各元素的目的。
组名相当于指针数
例如:
int *p,a[10];
p=a; //相当于 p=&a[0];
说明:数组名 a 相当于数组首元素 a[0] 的地址,即 a 等价于 &a[0]等价于p。
上述语句定义了整型指针变量 p 和整型数组 a,并使 p 初始指向数组首元素 a[0]。
代码示例:
#include <stdio.h>
int main ()
{
int a[3] = {10, 100, 200};
int i, *p[3];//指向整数的指针数组
for ( i = 0; i < 3; i++)
{
p[i] = &a[i]; //赋值为整数的地址
}
for ( i = 0; i < 3; i++)
{
printf("Value of a[%d] = %d\n", i, *p[i] );
}
return 0;
}
在这里,把 p声明为一个数组,由 3个整数指针组成。 因此,p 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中
6、C 指针的算术运算(++、--、+、-)
int *p=1000
假设 p 是一个指向地址 为1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:
p++
在执行完上述的运算之后,p 将指向位置 1004,因为 p 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。
概括一下:
- 指针的每一次递增,它其实会指向下一个元素的存储单元。
- 指针的每一次递减,它都会指向前一个元素的存储单元。
- 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
代码示例:
#include <stdio.h>
int main ()
{
int a[] = {10, 100, 200};
int i, *p;
/* 指针中的数组地址 */
p = a;
for ( i = 0; i < 3; i++)
{
printf("存储地址:a[%d] = %x\n", i, p );
printf("存储值:a[%d] = %d\n", i, *p );
/* 指向下一个位置 */
p++;
}
return 0;
}