指针是c语言中比较灵活的数据结构,灵活地运用指针可以:
(1) 有效地表示复杂的数据结构.
(2) 动态分配内存.
(3) 更方便地使用字符串和数组.
(4) 直接处理内存地址.
§10.1 地址和指针的概念
1、 地址的概念
“地址”:每个内存单元(字节) 的编号。
“值”:内存单元中存入的内容。
2、对内存单元的存取有两种方式
(1)直接访问:直接访问变量内容。
(2)间接访问:借助中间变量。
指针变量的概念
指针就是地址。如果有一个变量专门用来存放另一个变量的地址(即指针),则它称为“指针变量”。
§10.2 变量的指针和指向变量的指针变量
注:
(1) 定义变量时标识符前面的“*”,表示该变量为指针变量,但指针变量名是p1、p2、p3,而不是*p1、*p2、*p3。
(2) 程序语句中"*" 表示"指向"。
(3) 指针变量只能指向同一个类型的变量。
2、指针变量的引用
&——取址运算符:获取内存单元的地址
*——指针运算符:获取内存单元的内容
例10.1
3、指针变量作为函数参数
例:读程序,写结果
10.3 数组与指针
概念
数组的指针是指数组的起始地址。
数组元素的指针是指数组元素的地址。
例10.5 输出数组全部元素(设a数组,整型,10个元素)
上图是动态图,具体可以自己推理。
(2)下标法
main ( )
{int a[10];
int i;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for (i=0;i<10;i++)
printf("%3d",a[i]);
}
(3)地址法
main ( )
{int a[10];
int i;
for (i=0;i<10;i++)
scanf("%d",a+i);
printf("\n");
for (i=0;i<10;i++)
printf("%3d",*(a+i));
}
数组名作函数参数
用数组名作参数,在调用函数时实际上是把数组的首地址传给形参,这样实参与形参共同指向同一段内存。因而调用过程中,如形参数组元素值发生变化也就使实参数组的元素发生了变化,但这种变化并不是从形参传回实参的,而是由于形参与实参共享同一段内存而造成的。
例10.7 将数组a中n个整数按逆序存放。
如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下几类情况:
(1) 形参和实参都用数组名:传递的是实参数组首地址,形参与实参共用同一段内存单元。
(2) 实参用数组名,形参用指针变量:通过指针变量值的改变可以指向实参数组的任一元素。
(3) 实参和形参都用指针变量:先使实参指针变量指向数组的首地址,然后将实参的值传给形参,通过指针变量值的改变可以使其指向数组的每个元素。
(4) 实参为指针变量,形参为数组名:设p为指针变量,令p=&a[0],p为实参,对应的形参x为数组名,则函数调用时将 p的值传给形参数组名x,也就是使其取得a数组的首地址,使 x数组和 a数组共用同一段内存单元。
例10.9 用选择法对10个整数排序。
(1) 实参用指针变量,形参用数组名
void sort(int x[],int n)
{int i,j,k,t;
for (i=0;i<n-1;i++)
{ k=i;
for (j=i+1;j<n;j++)
if (x[j]>x[k]) k=j;
if (k!=i)
{t=x[i];x[i]=x[k];x[k]=t;}
}
}
main ( )
{ int *p,i,a[10];
p=a;
for (i=0;i<10;i++)
scanf("%d",p++);
p=a;
sort(p,10);
for (p=a,i=0;i<10;i++)
printf("%d",*p++);
}
(2) 实参用数组名,形参用指针变量
void sort(int *x,int n)
{int i,j,k,t;
for (i=0;i<n-1;i++)
{ k=i;
for (j=i+1;j<n;j++)
if (*(x+j)>*(x+k)) k=j;
if (k!=i)
{ t=*(x+i);
*(x+i)=*(x+k);
*(x+k)=t;}
}
}
main ( )
{ int i,a[10];
for (i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for (i=0;i<10;i++)
printf("%3d",a[i]);
}
(3)实参用指针变量,形参也用指针变量
void sort(int *x,int n)
{int i,j,k,t;
for (i=0;i<n-1;i++)
{ k=i;
for (j=i+1;j<n;j++)
if (*(x+j)>*(x+k)) k=j;
if (k!=i)
{ t=*(x+i);
*(x+i)=*(x+k);
*(x+k)=t;}
}
}
main ( )
{ int *p,i,a[10];
p=a;
for (i=0;i<10;i++)
scanf("%d",p++);
p=a;
sort(p,10);
for (p=a,i=0;i<10;i++)
printf("%d",*p++);
}
例: 从10个数中找出其中最大值和最小值。
方法2:
main( )
{int i, a[10], max, min, *p1, *p2;
void fun(int b[], int n, int *a1, int *a2 ) ;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
max=min=a[0];
p1=&max; p2=&min;
fun(a, 10, p1, p2);
printf("max=%d,min=%d\n",*p1,*p2);
}
void fun(int b[], int n, int *a1, int *a2)
{ int i;
for (i=1;i<n;i++)
if (b[i]>*a1) *a1=b[i];
else if (b[i]<*a2) *a2=b[i];
}
指针变量处理二维数组
1.二维数组的地址
2.二维数组的指针
(1) 指向数组元素的指针变量。
例:用指针变量输出数组元素a[i][j]的值。
main( )
{ int a[2][3]={2,3,6,7,8,5};
int i,j,*p;
p=a;
scanf("%d %d",&i,&j);
printf("%3d",*((p+i*3)+j));
}
(2) 指向由m个整数组成的一维数组的指针变量。
例10.12 输出二维数组任一行任一列的元素的值。
main ( )
{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4],i,j;
p=a;
scanf("i=%d,j=%d",&i,&j);
printf("a[%d,%d]=%d\n'',i,j,*(*(p+i)+j));
}
(3) 二维数组的指针作函数参数。
例 求二维数组a[4][5]每列最大值
§10.4 字符串的指针和指向字符串的指针变量
字符串的表示形式
1.字符数组
例
main
{static char string[ ]="I Love China!";
printf("%s\n",string);
}
2.字符串指针
例
main
{char *string="I Love China!";
printf("%s\n",string);
}
例10.17 将字符串a复制到字符串b中。
方法1:
main ( )
{ static char a[]="I am a boy.",b[20];
int i;
for (i=0;*(a+i)!='\0';i++)
*(b+i)=*(a+i);
*(b+i)='\0';
printf ("string a is :%s\n",a);
printf ("string b is :%s\n",b);
}
方法2:
字符串指针作函数参数
例10.19 用函数调用实现字符串的复制。
方法1:
void copy_string(char from[],char to[])
{ int i=0;
while(from[i]!='\0')
{ to[i]=from[i];i++;}
to[i]='\0'; }
main( )
{static char a[ ]="I am a teacher.";
static char b[ ]="You are a student.";
printf("string_a=%s\nstring_b=%s\n",a,b);
copy_string(a,b);
printf("string_a=%s\nstring_b=%s\n",a,b);
}
方法2:
例: 有一个长度不大于40的字符串,已知其中总包 含两个字符"A",求处于两个字符"A"中间的字符个数, 及这些字符.
#include”stdio.h”
int f(char *x,char *y)
{ int i, n=0,j;
for(i=0;*(x+i)!='\0';i++)
if(*(x+i)=='A') break;
for(j=i+1;*(x+j)!=‘A’;j++)
{ *(y+n)=*(x+i); n++;};
*(y+n)='\0';
return(n);
}
main( )
{ char a[40],b[40];
int n;
gets(a);
n=f(a,b);
printf(“n=%d\n",n);
puts(b);
}
字符指针变量与字符数组的区别
1.字符指针变量中存放的是地址,决不是将字符串放到字符指针变量中。
2.赋值方式:对数组只能各个赋值或者直接初始化。
3.指针变量可以指向一个字符型数据,但如果未对它赋以一个地址值,则它并未具体指向哪一个字符数据。
4.指针变量的值是可以改变的,而数组名所指的地址是固定的。
5.用指针变量指向一个格式字符串。
例: 有两个字符串str1,str2, 编程序将str1,str2 中对应字符较大的放到数组str3中.
void ccc(char *s1,char *s2,char *s3)
{int i;
for(i=0;*(s1+i)!='\0'&&*(s2+i)!='\0';i++)
*(s3+i)=*(s1+i)>*(s2+i)?*(s1+i):*(s2+i);
if(*(s1+i)=='\0')
while(*(s2+i)!='\0') {*(s3+i)=*(s2+i);i++;}
if(*(s2+i)=='\0')
while(*(s1+i)!='\0') {*(s3+i)=*(s1+i);i++;}
*(s3+i)='\0';
}
main()
{char *str1="Chinese",*str2="India",str3[40];
ccc(str1,str2,str3);
puts(str3);
}