什么内存地址?
下图为邮箱(现实世界,非email)的地址:
我们都见过像这样挂在墙上的很多个邮箱,每个邮箱有一个房间编号,根据房间编号找到相应的邮箱投入信件或取出信件。
内存跟他类似,每个内存单元有一个地址(Address),内存地址是从0开始编号的整数,CPU通过地址找到相应的内存单元,取其中的指令或者读写其中的数据。
与邮箱不同的是,一个地址所对应的内存单元不能存很多东西,只能存一个字节,以前讲过的int、float等多字节的数据类型保存在内存中要占用连续的多个地址,这种情况下数据的地址是它所占内存单元的起始地址。
什么是指针?
指针是一个值为内存地址的变量。正如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 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