数组

一维数组

一维数组初始化

  • 在创建数组时,我们必须定义数组的类型和大小,数组的大小不能为0,数组中的元素类型都是相同的。
  • 数组长度必须是固定的,必须为常量或常量表达式,不能使用变量进行初始化。

一维数组初始化

  • 完整初始化:int arr[3] = {1, 2, 3};
  • 不完整初始化:int arr[3] = {1, 2};
  • 自动计算数组长度初始化:int arr[] = {1, 2, 3};

注意:

  • 静态初始化缺省情况将数组元素自动设置为0,自动初始化缺省情况下是未初始化的。
  • 数组初始化元素的个数不允许超过数组长度:int a[3] = {1,2,3,4}; // 不合法

字符数组的初始化

  • 最笨拙的方式:char message[] = { 'h','e','l','l','o',0 };

  • 快速初始化方式:char message[] = "hello";这种初始化方式与上面的初始化方式是等效的,并且默认会在末尾'\0'

  • 上面都是字符数组初始化的基本方式,如果要初始化字符串常量,则采用:char *message = "hello";

    初始化字符数组和字符串常量的区别,可以表示如下:

作符返回一个指向数组的指针,而不是一个指向数组第一个元素的指针的指针。

注意:

  • 数组名的值是一个指针常量,不能修改它的值。

    int a[5];
    int b[5];
    a = b; // 操作违法,数组名是指针常量,不能改变它的值
    

一维数组的访问

  • 一维数组是使用下标来访问的,下标从0开始。
  • 数组的大小通过计算可以得到: sz=sizeof(arr)/sizeof(arr[0]);
  • 一维数组还可以通过指针来访问。
int array[10];
int *ap = array + 2;
表达式 含义
ap 与之对等的表达式是&array[2]
*apy 与之对等的表达式是array[2]
ap[0] c的下标引用和间接表达式是一致的,此处可以理解为*[ap+(0)],也就是*ap,即array[2]
ap+6 与之对等的表达式是&array[8],或者array+8
*ap+6 与之对等的表达式是array[2]+6
*(ap+6) 与之对等的表达式是array[8]
ap[6] 与之对等的表达式是array[8]
&ap 表达式是合法的,但是找到与之对等的表达式,因为无法知道编译器将ap放在相对于array的什么位置
ap[-1] 与之对等的表达式是array[1]
ap[9] 这个表达式是非法的,访问越界

考虑下面的表达式

2[array]

这个表达式是合法的,将其转换成对等的间接访问表达式为*(array+2),也就是array[2]。但是为了程序的可读性,强烈不建议如此写法。

一维数组和指针

int a[5];
int *b;

声明一个数组时,编译器先根据指定的元素数量为数组保存内存空间,然后创建数组名,它的值是一个常量,指向这段空间的起始位置。

声明指针的时候,编译器只为指针本身保留内存空间,并且指针变量并没有被初始化指向任何的内存空间。

下两个声明都是正确的:

int    strlen (char *string );
int    strlen( char string[]);

如果函数需要知道数组的长度,那么它必须作为一个显示的形参传递给函数。

多维数组

当数组维数不止一个时,我们可以声明多维数组。

多维数组的存储顺序

int a[3][6]内存中的存储顺序为:

看作一个初始化列表。

数组长度自动计算

int array[][5] = {
{0,1,2,3},
{4,}
{5,6}
}

对于上面的多维数组定义,编译器能够自动推断出数组的第一个维度是3。

多维数组的数组名

多维数组的数组名也是个指针常量,但是和一维数组不同,多维数组的数组名是指向数组第一行的常量指针,而不是指向第一个元素。

int matrix[3][10];

matrix可以看作一个一维数组,包含3个元素,每个元素都是包含10个整型数的数组。

多维数组的下标

假设声明了int matrix[3][10];

表达式 含义
matrix[1][5] 表示数组的第二行第六个元素
matrix 指向数组的第一行,它的类型:指向包含10个整型元素的数组的指针
matrix+1 指向数组的第二行,它的类型:指向包含10个整型元素的数组的指针
*(matrix+1) 指向数组的第二行第一个元素的值,即&matrix[1][0],它的类型是:int*
*(matrix+1)+5 指向数组的第二行第六个元素的值,即&matrix[1][5],它的类型是:int*
*(*(matrix+1)+5) 取右值,即a[1][5]

指向多维数组的指针

int    a1[10],   	 *p1=a1;
int    a2[3][10],    *p2=a2;

第一个声明是正确的,第二个声明是不正确的,因为a2的类型不是 int*而是int (*p)[10]

所以上面的表达式应该更改为:

int a2[3][10];
int (*p2)[10] = a2;

它使p2指向a2的第一行。下面的两个声明都是使p2指向a2的第一个整型元素:

int *p2 = &a2[0][0]
int *p2 = &a2[0];

作为函数参数的多维数组

作为函数参数的多为数组名的传递方式和一维数组相同——实际传递的是数组的第一个元素。两者的区别是,多位数组的每个元素本身是一个数组,所以以下声明:

int    arr[3][10];
...
fun(arr);

参数arr的类型是指向包含十个整形元素的数组的指针。fun的原型为如下两种形式中的任何一种:

void    fun(int     (*arr)[10]);
void    fun(int     arr[][10]);

指针数组

int    *api[10];

因为下标引用的优先级高于间接访问,所以表达式先执行下标操作,所以api是某种类型的数组。对数组的某个元素执行间接访问操作后,得到一个整型,所以api的元素类型为指向整型的指针。

指针数组的应用,例如分析C语言中关键字的个数:

char const *keyword[] = {
    "do",
    "for",
    "if",
    "register",
    "return",
    "switch",
    "while"
};

#define N_KEYWOLD \
	(sizeof(keyword)/sizeof(keyword[0]))

存储结构:

如果使用矩阵存储关键字:

char const keyword[][9] = {
	"do",
	"for",
	"if",
	"register",
	"return",
	"switch",
	"while"
};

此时的存储结构:

两种形式存储结构的比较:

  • 如果存储的字符串长度差不多,那么采用矩阵形式更加紧凑。
  • 如果字符串长度千差万别,并且大多数很短,极少数很长,此时使用指针数组的形式将更加紧凑。
posted @ 2019-09-15 10:26  youngliu91  阅读(174)  评论(0编辑  收藏  举报