1.变量的地址
在计算机中,内存是连续的存储空间。为了便于对其中某个指定部分进行操作,要对内存进行编址,内存编址的基本单位为字节。对于程序中定义的变量,编译时根据它的类型给它分配一定程度的内存单元。分配给每个变量的内存单元的起始地址。编译后每个变量都对应一个变量地址。当引用一个变量时就是从该变量名对应的地址开始的若干单元取出数据;当给一个变量赋初值时,则是将数据按该变量定义的类型存入对应的内存单元,于是该变量的地址存入的内容即该变量的值。short int型数据占用2个字节,int型和float型数据各占用4个字节,double占用8个字节,char型占用1个字节。
例如: short int a = 10;
float t = 0.618;
x t
1500 1501 1502 1503 1504 1505
10 0.618
2.指针的概念
用变量名直接从它对应的地址存取变量的值,称为“直接访问”。若将变量x的地址存放在另一个变量P中,访问时先从P中取出变量x的地址,再按x的地址访问变量x的值,这种访问称为“间接访问”。C语言规定用一种特殊类型的变量来存放地址,这种类型就是指针类型。通过指针类型的变量可以实现间接访问,C语言将它们称为变量的指针。变量的指针就是变量的地址,即指针P存放变量x的地址。
因此对于一个内存单元而言,内存单元的地址即为指针,内存单元中存放的数据才是该单元的内容。访问内存地址其实是为了更方便的操作内存中的数据,以上面的数据为例,在1500地址中存放的是一个整型的变量x,其值为10。如果定义一个指针变量p,p的值定义为1500,那么我们就称p是指向变量x的指针。
指针其实就是一个地址,是一个地址常量。但是指针变量却可以被赋予不同的指针值,即不同的地址值,可以指向不同的地址单元,是个变量。即指针变量专门用于存储其他变量地址的变量,定义指针的目的是为了通过指针去访问内存单元,对内存单元中的数据进行操作。
指针变量的值是一个地址,这个地址不仅可以使变量的地址,也可以是其他数据结构的首地址。用指针指向某种数据结构,其实就是将该数据结构的首地址赋予指针,因为许多数据结构都是连续存放的。所以通过访问指针变量取得了该数据结构的首地址,就可以访问到该数据结构的所有成员。这样就可以用一个指针变量来表示数据结构,只要该指针变量中赋予数据结构的首地址即可。
二. 指针变量的定义和初始化
1.指针变量的定义
指针变量定义的一般格式:
数据类型名 *指针变量名1,*指针变量名2,...;
指针变量定义时在变量名前加"*"表示
例如:
int * p;
定义了一个指向整型变量的指针p(注意:不是*p),它是一个指针变量,它所代表的是它所指向的整形变量的地址,具体它指向是哪个整形变量的地址,这是由该指针变量的初始化工作来决定的。又如:
int *p1; float *p2; char *p3;
指针指向的数据类型一旦定义后不能改变,且定义多个指针时可以写作 int *p1,*p2;(不是int *p1,p2;)
2.指针变量的初始化
当定义指针变量时,指针变量的值是随机的,不能确定它具体的指向,必须为其赋值才有意义。若使用未经赋值的指针变量将造成系统混乱,甚至出现死机。指针变量的赋值只能赋予地址,绝不能赋予任何其他数据,否则将引起错误。在C语言中,变量的地址是有编译系统分配的,对用户完全透明,用户不知道变量的具体地址。C语言中提供了地址运算符"&"来表示变量的地址。
指针初始化的一般格式:
数据类型名 *指针变量值 = 初始的地址值;
例如:
int a;
int *p1;
*p1 = &a;
或者:
float b;
float *p2 = &b;
然而 float *p = &c; 或者 int a; float *p = &a; 是错误的
可以将一个地址的值付给另一个地址: int *p2 = p1;
p1 = &a;是将a的地址赋给p1,*p1 = 3;是给p1所指向的变量赋值为3两者的意义完全不同。
int *p = 0; 在初始化中可以将一个指针初始化为一个空指针。
3.指针变量的引用和运算
&和*互为逆运算。
#include<stdio.h>
int main()
{
int *p1,*p2,*p,a,b;
scanf(,&a,&b);
p1 = &a, p2 = &b;
if(a<b){
p = p1;
p1 = p2;
p2 = p;
}
printf("\na=%d,b = %d\n",a,b);
printf("max = %d, min = %d\n",*p1,*p2);
return 0;
}
4.指针的运算
(1)指针变量赋值
将一个变量的地址付给一个指针变量。
int *p;
p = &a; //将变量a的地址赋给p
p = array; //将数组array的首地址赋给p
p = &array[i]; //将数组array第i个元素的地址赋给p
p1 = p2; //p1和p2都是指针变量,将p2的值赋给p1
#include<stdio.h>
int main()
{
int a,*pa;
pa = &a;
printf("\na的地址为:%p",&a);
printf("\npa = %p",pa);
printf("\npa的地址:%p",&pa);
return 0;
}
程序结果为:
a的地址为:0012FF7C
pa = 0012FF7C
pa的地址:0012FF78
(2)指针的取值
可以通过"*"运算符来去除相应地址中的变量值
(3)指针变量加(减)一个数
指针加减法与一般加减法规则不同,它是将整数和指针所指类型的字节数相乘,再加到指针所指向内存单元的地址上。这种运算适合与数组的运算,因为数组的内存空间的申请是连续的。所以一个指针变量加(减)一个整数并不是简单地将原址简单的加减一个整数。
(4)指针的自增自减
指针变量也可以进行增加和减小的运算,即通过++和--运算符来运算。运算规则为:指针所指向内存单元的地址上加上或者减去该指针所指向内存单元的字节数。在数组运算中,++和--可以指向前一个和后一个元素。
(5)空值运算
空值运算是指该指针变量不指向任何变量,即:
p = NULL;
(6)两个指针变量相减
两个指针变量相减,结果为两个指针的差值,采用这种方法可以得到两个元素之间的差值。差值的单位指的是指针所指向内存的大小。两个指针变量进行有效减法运算的前提条件是两个指针变量指向同一个数组,这样才有意义,若指向不同的数组则不仅没有意义,还会引起运行时的错误。
(7)两个指针变量比较
如果两个指针变量指向同一个数组的元素,则两个指针变量可以进行比较。指向前面的元素的指针变量“小于”指向后面的元素的指针变量。
注意:比较的前提条件是两个指针指向相同类型的变量。
int *p1,*p2,K;
int a[10] = {1,3,5,7,9,11,13,15,17,19};
p1 = a; //p1指向数组的首元素地址,即p=&a[0]
p2 = a; //p2指向数组的首元素地址,即p=&a[0]
p1++; //p1指向数组中的下一元素,即a[1]
K = *p1; //将p1所指元素赋给K,K = 3
K = *(p1+3) //将p1后第三个元素赋给K,K = 9,但p1本身不变
K = *p1 + 2 //将p1所指元素加2后赋给K,K = a[1]+2 = 5
if(p1>p2) //比较两个所指数组元素的下标
K = p1-p2 //将p1与p2所指元素下标相减,求得两元素间的下标差值
//此时p1指向的是a[1],p2指向的是a[0],则K = p1-p2 = 1-0 = 1