内存区的每一个字节都有一个编号,这就是“地址”。通过地址能找到所需的变量单元,可以说地址指向该变量单元。形象化的将地址称为“指针”
数据是分类型的,对不同类型的数据,在内存中分配的存储单元的大小(字节数)和存储方式是不同的(如整数是以二进制补码的形式存放,实数则以指数形式存放)
C语言中的地址包括位置信息(内存编号、或称纯地址)和它所指向的数据的类型信息,或者说是“带类型的地址”
指针和指针变量是两个概念:指针是一个地址,而指针变量是存放地址的变量。
所有的指针类型存储的都是内存地址,内存地址都是一个无符号十六进制整形数。(32位操作系统占4字节,64位操作系统占8字节)
定义指针变量:*类型名 指针变量名;左边的类型名是“基类型”,用来指定此指针变量可以指向的变量的类型。
典型实例:
#include<stdio.h> int main() { void sort(int *a,int *b,int *c); int a,b,c; printf("please enter three numbers:"); scanf("%d%d%d",&a,&b,&c); sort(&a,&b,&c); printf("The sorted numbers are:%d,%d,%d",a,b,c); return 0; } void sort(int *a,int *b,int *c) //形参是指针变量,定义sort函数,对三个整数进行排序 { if(*a<*b) swap(a,b); if(*a<*c) swap(a,c); if(*b<*c) swap(b,c); } void swap(int *x,int *y) //形参是指针变量,定义swap函数交换*x和*y的值 { int temp; //整型变量temp作为临时辅助变量实现*x和*y的值交换 temp=*x; *x=*y; *y=temp; } /* 错误写法: void swap(int *x,int *y) { int *temp; //*temp是指针变量temp所指向的值,未给temp赋值(野指针),*temp指向的值不可预见 *temp=*x; //对*temp赋值是向一个未知的储存单元赋值,这个储存单元里面可能存着一个有用的数据,可能会破坏系统的正常工作状态 *x=*y; *y=*temp; } void swap(int *x,int *y) //不能企图改变指针形参的值而改变指针实参的值 { int *temp; temp=a; a=b; b=temp } */
数组元素的指针就是数组元素的地址,引用数组元素可以用下标法(如a[3]),也可以用指针法(通过数组元素的指针找到所需的元素)
数组名不能代表整个数组,只代表数组中首元素的地址,是一个指针型常量。
在指针已指向一个数组元素时可以进行以下运算
设p开始时指向数组a的首元素(即p=a)
p++; *p;
p++使p指向下一元素a[1],然后在执行*p,则得到下一个元素a[1]的值
*p++
由于*和++同级优先,结合方向为自右向左,因此等价于*(p++)
for(i=0;i<10;i++,p++) printf("%d",*p) 等价于: for(i=0;i<10;i++) printf("%d",*p++)
作用都是先输出*p的值,再使p加1
*(p++)和*(++p)作用是不一样的,*(p++)是先取p的值,再使p加1。*(++p)是先使p加1,再取*p
*(arr+i)和arr[i]是无条件等价的
函数的形参会退化为指针,失去精度(不知道数组元素个数了)
fun(int arr[],int n) { …… } 等价于 fun(int *arr,int n) { …… }
实参类型 | 变量名 | 数组名 |
---|---|---|
要求形参的类型 | 变量名 | 数组名或指针变量 |
传递的信息 | 变量的值 | 实参数组首元素的地址 |
通过函数调用是否能改变实参的值 | 不能改变实参变量的值 | 能改变实参数组的值 |
例:将数组arr中的n个整数按相反顺序存放
#include<stdio.h> int main() { void exchange(int arr[],int n); int arr[]={10,9,8,7,6,5,4,3,2,1}; int i; printf("The original array:\n"); for(i=0;i<10;i++) printf("%d ",arr[i]); exchange(arr,10); //数组名是数组首元素地址,注意arr不是指针变量,是一个指针类型常量 printf("\nThe array have been exchanged:\n"); for(i=0;i<10;i++) printf("%d ",arr[i]); return 0; } void exchange(int arr[],int n) //exchange函数中的n用来接收需要处理的元素的个数,形参用数组名表示 //函数原型还可以写成void exchange(int *arr,int n),形参用指针变量表示 { int i,j,temp; for(i=0,j=9;i<10;i++,j--) {temp=0; if(i<j) { temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; }else break; } }
如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参有以下四种对应关系。(实际上都是地址的传递)
例:用指针的方法对十个整数按由大到小的顺序排序
#include<stdio.h> #define N 10 int main() { void sort(int *x,int n); int arr[N]={6,3,9,2,5,7,4,8,1,10}; int i,*p; p=arr; //指针变量指向a[0] printf("\n The original numbers are:\n"); for(i=0;i<10;i++) printf("%d ",arr[i]); sort(p,N); printf("\n The sorted numbers are:\n"); for(i=0;i<10;i++) printf("%d ",arr[i]); return 0; } void sort(int x[],int n) //定义sort函数,x是形参数组名 { int i,j,temp; for(i=0;i<N-1;i++) {temp=0; for(j=0;j<N-i-1;j++) { if(x[j]<x[j+1]) { temp=x[j]; x[j]=x[j+1]; x[j+1]=temp; } } } }
int a[3][4]={{1,3,5,7},{9,11,13,17},{19,23,27,31}};
a是二维数组名。a包含3行,即3个行元素:a[0],a[1],a[2]。每个行元素又是一个一维数组,它包含4个元素(即4个列元素),可以认为二维数组是“数组的数组”。
从二维数组角度看a代表的是二维数组首元素的地址,a+1代表序号为1的行的起始地址(即a[1]),a+2代表a[2]的起始地址。假设a=2000,则a+1=a[1]=2016,a+2=a[2]=2032
a[0],a[1],a[2]是一维数组名,代表首元素地址。
a[0]+0,a[0]+1,a[0]+2,a[0]=+3 分别是a[0][0],a[0][1],a[0][2],a[0][3]元素的地址(即&a[0][0],&a[0][1],&a[0][2],&a[0][3])
a[0]和*(a+0)等价,a[1]和*(a+1)等价,a[i]和*(a+i)等价。因此a[0]+1和*(a+0)+1等价都是&a[0][1]
(a[0]+1)是a[0][1]的值,同理*(*(a+0)+1)或*(*a+1)也是a[0][1]的值,即**(a[i]+j)或*(*(a+i)+J)是a[i][j]的值**
**C语言的地址信息中既包含位置信息(内存编号),还包含他所指向的数据的类型信息。**a[0]是一维数组名,它是一维数组中起始元素的地址;a是二维数组名,它是二维数组首行起始地址,二者的纯地址是相同的,但它们的基类型是不同的,即它们所指向的数据的类型不同,前者是整型数据,后者是一维数组。如果用一个指针变量pt来指向此一维数组,应当这样定义:int (*pt)[4];
在二维数组中,a+i,a[i],*(a+i),&a[i],&a[i][0]的值相同,都表示同一地址,但基类型不同。
例:输出二维数组有关数据(地址和元素的值)
#include<stdlib.h> int main() { int a[3][4]={{1,3,5,7},{9,11,13,17},{19,23,27,31}}; printf("%d,%d\n",a,*a); //0行起始地址和0行0列元素地址 printf("%d,%d\n",a[0],*(a+0)); //0行0列元素地址 printf("%d,%d\n",&a[0],&a[0][0]); //0行起始地址和0行0列元素地址 printf("%d,%d\n",a[1],a+1); //1行0列元素地址和1行起始地址 printf("%d,%d\n",&a[1][0],*(a+1)+0); //1行0列元素地址 printf("%d,%d\n",a[2],*(a+2)); //2行0列元素地址 printf("%d,%d\n",&a[2],a+2); //2行起始地址 printf("%d,%d\n",a[1][0],*(*(a+1)+0)); //1行0列元素的值 printf("%d,%d\n",*a[2],*(*(a+2)+0)); //2行0列元素的值 return 0; } /*结果是: 6422000,6422000 6422000,6422000 6422000,6422000 6422016,6422016 6422016,6422016 6422032,6422032 6422032,6422032 9,9 19,19 */
指向数组元素的指针变量
int *p;
指向由m个元素组成的一维数组的指针变量
int (*P)[m];