C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系
该专栏主要介绍 C 语言的基本语法,作为《程序设计语言》课程的课件与参考资料,用于《程序设计语言》课程的教学,供入门级用户阅读。
目录
- C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系
- 一、指针与一维数组
- 二、指针与二维数组
- 三、指针与字符串
- 四、指针数组
定义一个数组后,系统为其分配一块连续的存储空间,每个元素按照下标的顺序存放其中。事实上,数组名是一个指针常量,它是分配给数组的存储空间的起始地址。访问数组元素通过数组名和下标配合完成。
一、指针与一维数组
假设有以下数组:
int a[10];
则数组 a 一共有 10 个数组元素,每个数组元素中可以存放一个整型数据,占两个字节。数组 a 在内存中一共占据 20 个字节的连续存储空间。
假设存储空间的起始地址为 2000,即数组名 a 的地址为 2000,则 a+0 是 a[0] 的地址 2000(即:&a[0]),a+1 是 a[1] 的地址 2002(即:&a[1]),以此类推。
例如:
#include<stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
printf("a的地址为:%p\n",a);
printf("a[0]的地址为:%p\n",&a[0]);
printf("a[1]的地址为:%p\n",&a[1]);
printf("a[2]的地址为:%p\n",&a[2]);
}
以上程序的运行结果如下:
有以下数组的定义:
int a[10], *p;
则以下赋值语句是等价的:
p=a;
p=&a[0];
可以在定义指针变量时赋初值,例如:
int a[10], *p=&a[0];
//等价于
int a[10], *p;
p=&a[0];
//或
int a[10], *p=a;
例如:
#include<stdio.h>
int main()
{
int a[10]={1,2,3,4};
int *p;
p=&a[0];
printf("%d",*(p+2));
}
以上程序的运行结果如下:
关于数组与指针的说明:
(1)在以上程序中,p、a、&a[0] 均指向同一存储单元,都是数组 a 的首地址,也是第一个数据元素 a[0] 的地址。p 是变量,而 a 和 &a[0] 是常量。
(2)如果 p 的值是 &a[0],则 p+i 和 a+i 都是数组元素 a[i] 的地址,或者说它们都指向 a 数组的第 i 个元素。*(p+i) 或 *(a+i) 就是 p+i 或 a+i 所指向的数组元素,即 a[i]。例如:*(p+5) 或 *(a+5) 或就是 a[5]。
(3)指向数组的指针变量也可以带下标。如 p[i] 与 *(p+i) 等价。
例如:
#include<stdio.h>
int main()
{
int a[10]={1,2,3,4};
int *p=a;
printf("%d\n",p[2]);
printf("%d\n",*(p+2));
}
以上程序的运行结果如下:
(4)指针变量可以实现本身值的改变,如 p++ 是合法的,而 a++ 是错误的。因为 a 是数组名,它是数组的首地址,是常量。
(5)要注意指针变量的当前值。例如:
#include<stdio.h>
int main()
{
int a[10],i,*p=a;
for(i=0;i<10;i++){
*p++=i; //*p++ 等价于 *(p++)
}
p=a;
for(i=0;i<10;i++){
if(i%4==0){
printf("\n");
}
printf("a[%d]=%d ",i,*p++);
}
}
/*
如果 p 当前指向 a 数组中的第 i 个元素,则:
(1)*(p--) 相当于 a[i--];
(2)*(++p) 相当于 a[++i];
(3)*(--p) 相当于 a[--i];
*/
以上程序的运行结果如下:
二、指针与二维数组
设有如下二维数组:
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
各数组元素的地址如下:
#include<stdio.h>
int main()
{
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
int i,j,*p;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("&a[%d][%d]=%p ",i,j,&a[i][j]);
}
printf("\n");
}
printf("\n");
p=a[0];
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("&a[%d][%d]=%p ",i,j,p++);
}
printf("\n");
}
}
以上程序的运行结果如下:
数组 a 可分解为三个一维数组,即 a[0]、a[1]、a[2],每个一维数组又包含 4 个数组元素。例如:a[0] 数组,包含 a[0][0]、a[0][1]、 a[0][2]、 a[0][3] 四个元素。
从二维数组的角度来看,a 是二维数组名,a 代表整个二维数组的首地址,也是二维数组 0 行的首地址。a+1 代表第一行的首地址,a+2 代表第二行的首地址。
a[0] 是第一个一维数组的数组名和首地址。&a[0][0] 是二维数组 a 的 0 行 0 列的元素地址。
数组元素地址测试 :
#include<stdio.h>
int main()
{
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
printf("a的地址:%p\n",a);
printf("a[0]的地址:%p a[0][0]的地址:%p\n",a[0],&a[0][0]);
printf("a[1]的地址:%p a[1][0]的地址:%p\n",a[1],&a[1][0]);
printf("a[2]的地址:%p a[2][0]的地址:%p\n",a[2],&a[2][0]);
}
以上程序的运行结果如下:
a、a[0]、*(a+0)、*a、&a[0][0] 的值是相等的。同时 a+i、a[i]、*(a+i)、&a[i][0] 的值也是相等的。
测试代码如下:
#include<stdio.h>
int main()
{
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
printf("a的地址:%p a+2的地址:%p\n",a,a+2);
printf("a[0]的地址:%p a[2]的地址:%p\n",a[0],a[2]);
printf("*a的地址:%p *(a+2)的地址:%p\n",*a,*(a+2));
printf("&a[0][0]的地址:%p &a[2][0]的地址:%p\n",&a[0][0],&a[2][0]);
}
以上程序的运行结果如下:
a[0] 可以看成是 a[0]+0,是一维数组 a[0] 的 0 号元素的地址,而 a[0]+1 则是 a[0] 的 1 号元素的地址,由此可以得出 a[i]+j 是一维数组 a[i] 的 j 号元素的地址,等于 &a[i][j]。
由 a[i]=*(a+i) 得 a[i]+j=*(a+i)+j。由于 *(a+i)+j 是二维数组 a 的第 i 行第 j 列元素的地址,所以,该元素的值等于 *(*(a+i)+j)。
测试代码如下:
#include<stdio.h>
int main()
{
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
printf("a[1]+2的地址:%p &a[1][2]的地址:%p\n",a[1]+2,&a[1][2]);
printf("a[1][2]的值:%d *(a[1]+2)的值:%d\n",a[1][2],*(a[1]+2));
}
以上程序的运行结果如下:
定义指向二维数组的指针变量。
把二维数组 a 分解为一维数组 a[0]、a[1]、a[2] 之后,设 p 为指向二维数组的指针变量,格式为:
类型说明符 (*指针变量名)[长度];
说明:
(1)类型说明符:表示所指向数组的数据类型。
(2)*:表示其后的变量时指针类型。
(3)长度:表示二维数组分解为一维数组时一维数组的长度,也就是二维数组的列数。
(4)(*指针变量名):不能省略括号,如省略括号则表示是指针数组。
例如:
int (*p)[4];
以上定义表示 p 是一个指针变量,它指向包含 4 个元素的一维数组。若令 p=a,则 p+i 指向一维数组 a[i],*(p+i)+j 是二维数组第 i 行第 j 列的元素的地址,而 *(*(p+i)+j) 则是第 i 行第 j 列的值。
例如:
#include<stdio.h>
int main()
{
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
int i,j;
int (*p)[4];
p=a;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%8d",*(*(p+i)+j));
}
printf("\n");
}
}
以上程序的运行结果如下:
三、指针与字符串
用字符指针指向一个字符串。如下所示:
#include<stdio.h>
int main()
{
char str1[]="I am a student.";
//str1是字符数组名,它代表字符数组的首地址
char *str2="This is a book.";
//首先定义指向字符串的指针变量str2,并把
//字符串的起始地址存入str2
printf("%s\n",str1);
printf("%s\n",str2);
}
指向字符串的指针变量的定义与指向字符变量的指针变量的定义方法相同。以上程序的运行结果如下:
输出字符串中第 5 个字符以后的所有内容。程序如下:
#include<stdio.h>
int main()
{
char str1[]="Management";
char *str2="Classroom",*pch;
pch=str1;
str2+=5;
pch+=5;
printf("%s\n",pch);
printf("%s\n",str2);
}
以上程序的运行结果如下:
用字符数组和字符型指针变量都可以实现字符串的存储和运算。但两者是有区别的。
(1)字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身存放在以该首地址为首的一块连续的内存空间中,并以 ‘\0’ 作为结束符。字符数组是由若干个数组元素组成的,可用来存放整个字符串。
(2)可以向字符串指针变量赋值。例如:
char *ps;
ps="C program.";
四、指针数组
指针数组是指一个数组的元素值为指针。指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。定义指针数组的格式如下:
类型说明符 *数组名[长度];
说明:类型说明符表示指针所指向的变量的类型。
例如:
int *p[3];
以上定义表示 p 是一个指针数组,它有三个数组元素,每个元素都是一个指针,指向整型变量。
例如:利用一个指针数组来指向一个二维数组,并通过指针数组输出二维数组中的元素的值。
#include<stdio.h>
int main()
{
int a[3][3]={1,2,3,4,5,6,7,8,9};
int i,j,*p[3];
for(i=0;i<3;i++){
p[i]=a[i];
}
for(i=0;i<3;i++){
for(j=0;j<3;j++){
printf("%8d",*(p[i]+j));
}
printf("\n");
}
}
以上程序的运行结果如下:
上面的例子中,p 是一个指针数组,三个元素分别指向二维数组 a 的各行。
指针数组与二维数组指针变量的区别:二维数组指针变量是单个的变量,定义形式为:(*p)[n];而指针数组类型表示的是多个指针,定义形式为:*p[n]。例如:
int (*p)[3]; //表示指向二维数组的指针变量。该二维数组的列数为3
int *p[3]; //表示 p 是一个指针数组,有三个下标变量 p[0]、p[1]、p[2],均为指针变量
指针数组常用来表示一组字符串,指针数组的每个元素被赋予一个字符串的首地址。指向字符串的指针数组的初始化更为简单。例如:
#include<stdio.h>
int main()
{
char *name[]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
int i;
for(i=0;i<7;i++){
printf("%s\n",name[i]);
}
}
以上程序的运行结果如下: