-
数组元素的指针
- 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址
- 指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中)
- 所谓数组元素的指针就是数组元素的地址
-
在引用数组元素时指针的运算
在指针指向数组元素时,允许以下运算:
- 加一个整数(用+或+=),如p=p+1
- 减一个整数(用-或-=),如p=p-1
- 自加运算,如p++,++p
- 自减运算,如p--,--p
- 两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)
-
通过指针引用数组元素
引用一个数组元素,可用下面两种方法:
(1) 下标法,如a[i]形式
(2) 指针法,如*(a+i)或*(p+i)
其中a是数组名,p是指向数组元素的指针变量,其初值p=a
例: 有一个整型数组a,有10个元素,要求输出数组中的全部元素。
解题思路:引用数组中各元素的值有3种方法:
(1)下标法;
(2)通过数组名计算数组元素地址,找出元素的值;
(3) 用指针变量指向数组元素
分别写出程序,以资比较分析。
下标法
#include <stdio.h>
int main()
{
int a[10];
int i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf(“%d ”,a[i]);
printf("%\n");
return 0;
}
通过数组名计算数组元素地址,找出元素的值
#include <stdio.h>
int main()
{
int a[10];
int i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++)
scanf("%d",a+i);
for(i=0;i<10;i++)
printf(“%d ”,*(a+i));
printf("\n");
return 0;
}
用指针变量指向数组元素
#include <stdio.h>
int main()
{
int a[10];
int *p,i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(p=a;p<(a+10);p++)
printf(“%d ”,*p);
printf("\n");
return 0;
}
3种方法的比较:
① 第(1)和第(2)种方法执行效率相同
C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。
因此用第(1)和第(2)种方法找数组元素费时较多。
② 第(3)种方法比第(1)、第(2)种方法快
用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的
这种有规律地改变地址值(p++)能大大提高执行效率
③ 用下标法比较直观,能直接知道是第几个元素。
用地址法或指针变量的方法不直观,难以很快地判断出当前处理的是哪一个元素。
用数组名作函数参数
- 用数组名作函数参数时,因为实参数组名代表该数组首元素的地址,形参应该是一个指针变量
- C编译都是将形参数组名作为指针变量来处理的
-
通过指针引用多维数组
指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。
例: 二维数组的有关数据(地址和值)
int a[3][4]={1,3,5,7,9,11,13,15, 17,19,21,23};
分析下例情况:
- a,*a, a+1,*a+1
- a[0],*(a+0), a[0]+1,*(a+0)+1
- &a[0],&a[0][0], &a[0]+1,&a[0][0]+1
- a[1],a+1, a[1]+1,(a+1)+1
- &a[1][0],*(a+1)+0, a[1][0]+1,*(a+1)+0+1
- a[2],*(a+2), a[2]+1,*(a+2)+1
- &a[2],a+2, &a[2]-1,a+2-1
- a[1][0],*(*(a+1)+0), a[1][0+1],*(*(a+1)+0+1)
- *a[2],*(*(a+2)+0), *(a[2]+1),*(*(a+2)+0+1)
-
数组指针
数组指针(也称行指针)
数组指针变量的定义:
类型 (*数组指针变量名)[常量表达式];
例如:int n= 4;
int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
-
指针数组
指针数组变量的定义:
类型 *指针数组变量名[常量表达式];
例如:int n= 4; int a[3][4];
int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给指针数组:
Øint *p[3];int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++) p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]所以要分别赋值。
数组指针和指针数组的区别:数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
#include <stdio.h>
int main(void)
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int (*p1)[4] = a; //定义数组指针p1,指向一个含有4个
//元素的一维数组,现指向二维数组
//a的第0行的一维数组的首地址
for(int i=0;i<3;i++)
for(int j=0;j<4;j++)
printf("%d,",p1[i][j]);
printf("\n");
return 0;
}
#include <stdio.h>
int main(void)
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int *p2[3]; //定义指针数组p2,数组长度为3,可以保
//存3个指针(地址)元素
p2[0] = a[0]; //数组p2的第0个指针元素保存a[0]的地址
p2[1] = a[1]; //数组p2的第1个指针元素保存a[1]的地址
p2[2] = a[2]; //数组p2的第2个指针元素保存a[2]的地址
for(int i=0;i<3;i++)
for(int j=0;j<4;j++)
printf("%d,",p2[i][j]);
printf("\n");
return 0;
}
& 符号的意思是取地址,也就是返回一个对象在内存中的地址。* 符号的意思是取得一个指针所指向的对象。 也就是如果一个指针保存着一个内存地址,那么它就返回在那个地址的对象。所以当你这么写时 *ipp = ip2,实际上是把 ipp 存的地址所对应的对象,也就是 ip1 取到,然后把 ip2 存的值赋值给 ip1,也就是 j 的地址。简单点就是:&:取址。* :取值。
例: 有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针的指针变量,输出整型数组各元素的值。
-
字符串的引用方式
字符串是存放在字符数组中的。引用一个字符串,可以用以下两种方法。
(1) 用字符数组存放一个字符串,可以通过数组名和格式声明“%s”输出该字符串,也可以通过数组名和下标引用字符串中一个字符。
(2) 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
例: 定义一个字符数组,在其中存放字符串“I love China!”,输出该字符串和第8个字符。
解题思路:
- 定义字符数组string,对它初始化,由于在初始化时字符的个数是确定的,因此可不必指定数组的长度。
- 用数组名string和输出格式%s可以输出整个字符串。
- 用数组名和下标可以引用任一数组元素。(方法1)
- string改成字符指针变量的形式定义(方法2)
-
字符指针作函数参数
- 如果想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。
- 在被调用的函数中可以改变字符串的内容
- 在主调函数中可以引用改变后的字符串。
例:用函数调用实现字符串的复制。
解题思路:
定义一个函数copy_string用来实现字符串复制的功能,在主函数中调用此函数,函数的形参和实参可以分别用字符数组名或字符指针变量。分别编程,以供分析比较。
(1) 用字符数组名作为函数参数
#include <stdio.h>
void copy_string(char from[],char to[]);
int main()
{
char a[]="I am a teacher.";
char b[]="you are a student.";
printf(“a=%s\nb=%s\n",a,b);
printf("copy string a to string b:\n");
copy_string(a,b);
printf(“a=%s\nb=%s\n",a,b);
return 0;
}
void copy_string(char from[], char to[])
{
int i=0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
(2)用字符型指针变量作实参
copy_string不变,在main函数中定义字符指针变量from和to,分别指向两个字符数组a,b。
仅需要修改主函数代码
#include <stdio.h>
void copy_string(char from[], char to[]);
int main()
{
char a[]=“I am a teacher.”;
char b[]=“you are a student.”;
char *from=a,*to=b;
printf(“a=%s\nb=%s\n",a,b);
printf("\ncopy string a to string b:\n");
copy_string(from,to);
printf(“a=%s\nb=%s\n",a,b);
return 0;
}
void copy_string(char from[], char to[])
{
int i=0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
(3)用字符指针变量作形参和实参
#include <stdio.h>
void copy_string(char *from, char *to);
int main()
{
char *a=“I am a teacher.”;
char b[]=“You are a student.”;
char *p=b;
printf(“a=%s\nb=%s\n”,a,b);
printf("\ncopy string a to string b:\n");
copy_string(a,p);
printf(“a=%s\nb=%s\n”,a,b);
return 0;
}
void copy_string(char *from, char *to)
{
for( ;*from!='\0'; from++,to++)
{
*to=*from;
}
*to='\0';
}
使用字符指针变量和字符数组的比较
用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。
(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。
(7) 引用数组元数
对字符数组可以用下标法和地址法引用数组元素(a[5],*(a+5))。如果字符指针变量p=a,则也可以用指针变量带下标的形式和地址法引用(p[5],*(p+5))。
char *a=″I love China!″;
则a[5]的值是第6个字符,即字母’e’
(8) 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
char *format;
format=”a=%d,b=%f\n”;
printf(format,a,b);
相当于
printf(“a=%d,b=%f\n”,a,b);