二维表的数据结构可以用二维数组、指针数组、结构体来表示。
1 二维数组
数组的每一个元素又是数组的数组称为多维数。
最常用的多维数组是二维数组,又称为矩阵。
二维数组的定义格式:
类型说明 数组名[常量表达式1][常量表达式2]
存放次序:按行存放。
二维数组使用两个下标来标识特定的元素。用二维数组可以用来表示一个行、列的数值表。依据惯例,数组的第一个下标表示行,称为行下标,第二个下标表示元素的列,称为列下标。如一个3行4列的数组:
第0列第1列第2列第3列第0行a[0][0]a[0][1]a[0][2]a[0][3]第1行a[1][0]a[1][1]a[1][2]a[1][3]第2行a[2][0]a[2][1]a[2][2]a[2][3]用一个实例来说明:
#include <stdio.h>
#include <conio.h>
void main()
{
int a[5][5]={
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20},
{21,22,23,24,25}};
int (*p)[5];
p =a;
printf("&a[0][0]=%x\n", &a[0][0]);
printf("p=%x\n", p);
printf("*a=%x\n", *a);
printf("a=%x\n", a);
printf("**a=%d\n", **a);
printf("a[0][0]=%d\n", a[0][0]);
printf("**p=%d\n", **p);
printf("\n");
printf("&a[1][0]=%x\n", &a[1][0]);
printf("a[1]=%x\n", a[1]);
printf("a[0]+1=%x\n", a[0]+1);
printf("a+1=%x\n", a+1);
printf("p+1=%x\n", p+1);
printf("\n");
printf("a[1][0]=%d\n", a[1][0]);
printf("a[1][0]+1=%x\n", a[1][0]+1);
printf("**(a+1)=%x\n", **(a+1));
printf("*(p+1)[0]=%x\n", *(p+1)[0]);
getch();
}
运算结果:
&a[0][0]=12fee4
p=12fee4
*a=12fee4
a=12fee4
**a=1
a[0][0]=1
**p=1
&a[1][0]=12fef8
a[1]=12fef8
a[0]+1=12fee8
a+1=12fef8
p+1=12fef8
a[1][0]=6
a[1][0]+1=7
**(a+1)=6
*(p+1)[0]=6
说明:
a是一个数组名,类型是指向一维数组的指针,不是变量,a的值是指针常量,即不能有a++或者a=p这些操作。a指向这块连续空间的首地址,值是&a[0][0]。
a[0]是一维数组名,类型是指向整型的指针,值是&a[0][0],这个值是一个常量。
a[1]是一维数组名,类型是指向整型的指针,值是&a[1][0],这个值是一个常量。
p是一个数组指针变量,指向一维数组的指针变量,值是&a[0][0]。可以执行p++;p=a等操作。
a+1表示指向下一行元素,也可以理解为指向下一个一维数组。加号前面如果是指针或地址、后面的数字代表指针偏移量,表示偏移该对象的一个子元素,如果该对象是一个二维数组,则其子元素是一个一维数组,则偏移一个一维数组的地址空间;如果该对象是一个一维数组,则只偏移一个数组元素所需要的地址空间。(当指针加上或减去一个整数时,指针增加或减少的实际值是该整数与该指针指向对象的字节数的乘积。)
*(a+1)是取出第一行的首地址。运算符*其实是对指针a+1的取值运算,而a是一个二维数组,相当于一个双重指针,a+1是对地址的偏移,所以取值后还是一个指针或地址。
a[0]+1是指向第0行第1个元素,也可以理解为指向一维数组a[0]的第一个元素。
p+1同a+1
*(p+1)同*(a+1)
虽然a跟a[0]值是一样,但类型不一样,表示的意义不一样。通过分析就不难理解为什么*(*(a+i)+j)和a[i][j]等效了。
对于二维数值的操作,如赋值、求和,通常可以用一个嵌套的内、外for循环来操作,其内、外循环的计数器对应行、列数。
因为数组元素在内存中都是按序排放的,所以当用数组做为形参时,用like[][5]表示即可,也就是需要知道列数,照此可以推算出数组的第n个元素的行、列下标。其中的每一行都是一个一维数组。
2 指针数组
指针本身也是变量,他们也可以像其他变量一样存储在数组中
一个数组,如果他的元素均为指针,则称为指针数组
一维指针数组的定义形式:
类型名 *数组名[数组长度];
例如,char *string[10]; 定义了一个名为string的指针数组,该数组有10个元素,数组的每个成员是一个指向字符的指针。
下面通过一个实例来说明:
#include <stdio.h>
#include <conio.h>
//注意指针数组和数组指针分别是如何指向二维数组的
main()
{
static int m[3][4]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}};
int (*p)[4];//数组指针 p是指针,指向一维数组,每个一维数组有4个int元素*p是个数组的地址,**p就是数组元素了
int i,j;
int *q[3];//指针数组 q是数组,数组元素是指针,3个int指针
p=m; //p是指针,可以直接指向二维数组
printf("--通过数组指针、双重循环、行i列j、*(*(p+i)+j))输出元素:--\n");
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("*(*(p+%d)+%d)=%2d ",i,j,*(*(p+i)+j));
}
printf("\n");
}
printf("注:p+i相当于第i行的首地址;\n");
printf("&m[0][0]=%x\n",&m[0][0]);
printf("&m[1][0]=%x\n",&m[1][0]);
printf("p+1=%x\n",p+1);
printf("*(p+1)=%x\n",*(p+1));
printf("\n");
printf("--通过数组指针、循环行i、*(*p+j)输出元素:--\n");
for(i=0;i<3;i++,p++)//p可看成是行指针
{
printf("**p:%x=%2d ",p,**p);//每一行的第一个元素
printf("*(*p+1):%x=%2d ",*p+1,*(*p+1));//每一行的第二个元素
printf("*(*p+2):%x=%2d ",*p+2,*(*p+2));//每一行的第三个元素
printf("*(*p+3):%x=%2d ",*p+3,*(*p+3));//每一行的第四个元素
printf("\n");
}
printf("\n");
printf("--指针数组*q[3],输出元素q[i][j]或*(q[i]+j)--\n");
for(i=0;i<3;i++)
q[i]=m[i];//q是数组,元素q[i]是指针,m[i]是行的一维数组;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("q[%d][%d]=%2d ",i,j,q[i][j]);//q[i][j]可换成*(q[i]+j)
}
printf("\n");
}
printf("\n");
q[0]=m[0];
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("*(q[0]+%d+4*%d)=%2d ",j,i,*(q[0]+j+4*i));
}
printf("\n");
}
getch();
}
运算结果:
在指向二维数组的数组指针和表示二维数组的指针数组中,定义的名称相当于一个双重指针。符号[]与*是等价的,两个[]、两个**、一个[]一个*才可以指向具体的值,如果只有一个[]或*则还是一个指针,指向一个一维数组,代表行,其运算也是对地址的运算。
3 结构体
如何在程序中表示学生信息?
学号姓名语文成绩数学成绩英语成绩.00001张三96948800003李四89707600004王五908778用二维的数组来信息,该方案不可行,因为这些信息有不同的类型。
每一列用一个一维数组来表示,这种方法称为并联数组。要保证每位学生信息的正确性很难。
当我们考虑怎么逻辑地组织数据时,应该将一个人的所有信息项放在一起,即保持相关性。
结构体类型允许程序员把一些分量聚合成一个整体,用一个变量表示。
一个结构体的各个分量都有名字,把这些分量称为成员(member)。
由于结构体的成员可以是各种类型的,程序员能创建适合于问题的数据聚合。
定义结构体类型中包括哪些分量。
格式:
struct 结构体类型名{
字段声明;
};
如:
struct studentT {
char no[10];
char name[10];
int chinese;
int math;
int english;
};
字段名可与程序中的变量名相同。
在不同的结构体中可以有相同的字段名。
结构体成员的类型可以是任意类型,当然也可以是结构体类型。
结构体变量的定义和普通的变量定义一样。如定义了结构体类型studentT,就可以定义结构体变量:
studentT student1。
一旦定义了一个结构体类型的变量,系统在分配内存时就会分配一块连续的空间,依次存放它的每一个分量。这块空间总的名字就是结构体变量的名字。内部还有各自的名字。
结构体变量的初始化:
studentT student1={“00001”,“张三” ,87,90,77};
定义结构体类型的同时定义变量:
对结构体类型变量的引用一般为引用他的成员。
成员的表示:
结构变量名.成员名
如: student1.name
如结构中还有结构,则一级一级用”.”分开 ,如
如:student1.birthday.year
通过指针操作记录:
给结构体指针赋值,如:
sp = &student1;
结构体指针的引用:
student1->name;