指针

368 阅读4分钟

什么内存地址?

下图为邮箱(现实世界,非email)的地址:

邮箱的地址

我们都见过像这样挂在墙上的很多个邮箱,每个邮箱有一个房间编号,根据房间编号找到相应的邮箱投入信件或取出信件。

内存跟他类似,每个内存单元有一个地址(Address),内存地址是从0开始编号的整数,CPU通过地址找到相应的内存单元,取其中的指令或者读写其中的数据。

与邮箱不同的是,一个地址所对应的内存单元不能存很多东西,只能存一个字节,以前讲过的intfloat等多字节的数据类型保存在内存中要占用连续的多个地址,这种情况下数据的地址是它所占内存单元的起始地址。

什么是指针?

指针是一个值为内存地址的变量。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值为地址。

&是什么?

&是取地址运算符,用来获取变量的地址,通常在scanf函数和指针变量赋值时,频繁的使用取地址运算符。

int n;
scanf("%d",&n);
int a = 0;
int* p;
p = &a;

*是什么?

这个*,叫做间接运算符,有时也称为解引用运算符。使用间接运算符,可以找出存储在ban中的值。

int ban = 14;
int* p;         //p是指向int类型的变量的指针变量
p = &ban;
int ac = *p;
printf("%d",ac);

指针赋值

可以使用指针给指针复制,但前提时两个指针的数据类型相同。比如:

int i, j, *p, *q;		//声明变量
p = &i;			    	//把i的地址赋值给p

//把p的内容(即i的地址)赋值给q
q = p;					

效果图:

//现在p和q都指向了i,所以可以用对*p或*q赋新值的方法来改变i
*p = 1;

效果图:

*q = 2;

效果图:

注意不要把 p = q 和 *p = *q 搞混。
int i,j,*p,*q;
q = &i;
p = &j;
i = 1;
*p = *q 

第一条语句是指针赋值,而第二条语句不是。赋值语句 *p = * q是把 q 指向的值(i的值)赋值到 p 指向的对象(变量 j )中。

指针作为参数在函数间通信

比如如下代码,该函数传递的不是x和y的值,而是它们的地址。这意味着出现在函数add( )原型和定义中的形式参数 u 和 v 将把地址作为它们的值。因此,应把它们声明为指针。由于 x 和 y 是整数,所以u和v是指向整数的指针。

#include <stdio.h>

void add(int* u,int* v);

int&emsp;main(void)
{
int x = 5, y = 10;
add(&x, &y); // 把地址发送给函数
printf("Now x = %d and y = %d\n", x, y);
return 0;
}

void add(int* u,int * v)
{
*u ++;
*v ++;
}

程序结果:

Now x = 6 and y = 11

指针and数组

数组名是数组首元素的地址

也就是说,如果array是一个数组,那么下面的语句成立

if (array == &array[0]){
	printf("数组名是数组首元素的地址");
}

array 和 &array[0] 都表示数组首元素的内存地址(&是地址运算符),可以把它们赋值给指针变量,然后通过指针和间接寻址运算符指向数组中的变量的值。程序示例如下:

int a[10], *p;
p = a;   //此写法同等于 p = &a[0];

其结果如图所示:

*p = 5;   //使用指针 p 通过间接运算符(*)访问a[0]

其结果如图所示:

我们不仅可以如此操作,还可以通过 p 上执行指针算数运算(or地址算数运算)可以访问数组a的其他所有有元素。C语言支持3种(而且只有3种)格式的指针算术运算:

  • 指针加上整数
  • 指针减去整数
  • 两个指针相减
指针加上整数
int a[10],*p,*q;
p = &a[2];		//如图1
q = p + 3;		//如图2
p += 6;			//如图3

连个指针相减

两个指针相减,结果为指针之间的距离(用数组元素的个数来度量)。因此,如果 p 指向 a[i] 且 q 指向 a[j] ,那么 p - q 就等于 i - j 。例如:

int a[10],*p,*q;
p = &a[5];	
q = &a[1];	

int n,m;
n = p - q;		//n为4
m = q - p;		//m为-4