一、指针是什么
内存区的每一个字节都有一个编号,这就是“地址”。通过地址能找到所需的变量单元,可以说地址指向该变量单元。形象化的将地址称为“指针”
数据是分类型的,对不同类型的数据,在内存中分配的存储单元的大小(字节数)和存储方式是不同的(如整数是以二进制补码的形式存放,实数则以指数形式存放)
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)
{
if(*a<*b)
swap(a,b);
if(*a<*c)
swap(a,c);
if(*b<*c)
swap(b,c);
}
void swap(int *x,int *y)
{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
三、通过指针引用数组
数组元素的指针
数组元素的指针就是数组元素的地址,引用数组元素可以用下标法(如a[3]),也可以用指针法(通过数组元素的指针找到所需的元素)
数组名不能代表整个数组,只代表数组中首元素的地址,是一个指针型常量。
在引用数组元素时的指针运算
在指针已指向一个数组元素时可以进行以下运算
- 加一个整数(用+或+=),如p+1;
- 减一个整数(用-或-=),如p-1;
- 自加运算,如p++,–p
- 自减运算,如p–,--p
- 两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义)
- 数组元素是float型,每个元素占四字节。p+1意味着使p的值(是地址)加四个字节,以使他指向下一个元素。
- p2-p1的结果是:p2-p1的值(两个地址之差)初以数组元素的长度
利用指针引用数组元素
设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);
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)
{
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;
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)
{
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);
printf("%d,%d\n",a[0],*(a+0));
printf("%d,%d\n",&a[0],&a[0][0]);
printf("%d,%d\n",a[1],a+1);
printf("%d,%d\n",&a[1][0],*(a+1)+0);
printf("%d,%d\n",a[2],*(a+2));
printf("%d,%d\n",&a[2],a+2);
printf("%d,%d\n",a[1][0],*(*(a+1)+0));
printf("%d,%d\n",*a[2],*(*(a+2)+0));
return 0;
}
指向多维数组元素的指针变量
-
指向数组元素的指针变量 int *p; -
指向由m个元素组成的一维数组的指针变量 int (*P)[m];
|