转载

[C/C++基础] 1.数组

概述:

数组是一种数据格式,能够存储多个同类型的值。

一维数组可用来实现线性表的顺序存储、哈希表、堆(堆排序部分)等;

二位数组可用来保存图的邻接矩阵等。

1.1 一维数组的声明与字符数组

1.1.1 一维数组的声明与初始化

1.一维数组的声明

应指出以下三点:

1)存储在每个元素中的值的类型;

2)数组名;

3)数组中的元素数,数组的元素数必须用值大于等于1的常量表达式定义。

需要引起注意的地方: 数组定义中类型不能使引用,因为引用是不能赋值的,而数组中元素必须可以被赋值。

如下面的定义就是错误的。

int& a[10]; // 错误的

虽然没有引用数组,但数组可以有引用。

int a[6] = {0, 2, 4, 6, 8, 10}; int (&p)[6] = a;  // p是数组a的引用

注:数组的引用可以用于函数实参传递。此时可确保传递过来的数组长度合乎要求。

2.一维数组的初始化

1)函数体 定义的内置类型数组(即内置类型的全局数组),元素初始化为0;

2)函数体 定义的内置类型数组,元素无初始化(注意,若只初始化部分元素,其后的元素此时也会被初始化为0);

3)如果不是内置类型,则不管其在哪定义,自动调用其默认构造器函数为其初始化,若该类型无默认构造函数则会报错。

注:当数组的大小未知时,需要动态声明一维数组,声明格式如下:

int* a = new int[n]; //.... delete []a; // 当数组使用完毕,需要释放内存空间

1.1.2 字符数组

字符数组的初始化既可以用字符常量也可以用常量字符串(末尾有空字符)记性初始化:

方式1:char ca1[] = {'C', '+', '+'}; // 末尾没有null字符       char ca1[] = {'C', '+', '+', '/0'}; // 末尾显示添加null字符 方式2:char ca1[] = "C++"; // 末尾自动添加null字符

注:ca1的长度为3,ca2和ca3的长度为4。

const char ch4[5] = "Hello"; // 编译错误 数组长度应为6

另外,C/C++很多字符串处理函数(strcpy,strcat等),传递给这些标准库函数的指针必须是非零值并且是 指向以null结束的字符数组 的第一个元素。

1.2 二维数组

1.2.1 二位数组的声明与初始化

初始化方式分为两种:

1)按行初始化:

int a[3][4] = {{0, 1, 2, 3},                 {4, 5, 6, 7},                {8, 9, 10, 11}};

2)顺序初始化:

int a[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

注:如果只是初始化部分,则其余的规则按照一维数组的初始化规则。

C++规定,在声明和初始化一个二维数组时,如果对二维数组的所有元素都赋值,则第一维(行数)可以省略,但第二维一定不能省略。

1.2.2 行优先存储和列优先存储

本质上讲,所有数组在内存中都是一维线性的,C/C++中,二维数组按照行优先顺序连续存储, 因此按照行优先读取可以顺序读取,速度更快。

注:二维数组a[x][y]转化为一维数组b:a[x][y] = b[x*列数 + y]。

1.2.3 二维数组的动态声明

int **a = new int* [m]; for(int i = 0 ; i < m ; i++)     a[i] = new int[n];

动态声明的数组任意的a[k]都是一个int*类型,即一个地址,所以只能a[i][j]或者*(*(a+i)+j)来访问数组元素。

此外动态声明的数组,使用后需要释放内存。

for(int i = 0 ; i < m ; i++)     delete []a[i]; delete []a;

1.3 数组指针、指针数组、数组名的指针

1.3.1 指针运算---算数运算、关系运算

C/C++规定的合法运算包括:指针与整数的加减、同类型指针间的比较、同类型的两个指针相减。

指针与整数的加减

当一个指针和一个整数进行算数运算的时候,整数在执行加法运算前始终会根据合适的大小进行调整,这个“合适的大小”就是 指针所指向类型的大小

例如:float占据4个字节,在计算float型指针加3的表达式时,这个3将根据float的类型大小(即此例中的4)进行调整为12。

同类型的两个指针相减

减法运算的值是两个指针在内存中的距离(以数组元素长度为单位,不是以字节为单位)。

同类型的两个指针比较

进行<、<=、>、>=运算,前提是它们都指向同一个数组中的元素。根据所使用的操作符,比较表达式的值表示那个指针指向数组更靠前或靠后。

下面的代码用于清楚一个数组中所有的元素:

float values[5]; float *vp; vp = &values[0] ;  while(vp < &values[5]) {   *vp = 0;   *vp++; // 等价于 *vp ++= 0 }

1.3.2 指针数组与数组指针

指针数组 :一个数组里面装着指针,即指针数组为一个数组。

例如一个有10个指针的数组,其中每个指针是指向一个整数型,那么此数组的定义为:

int *a[10];

数组指针:一个指向数组的指针,它其实还是指针,只不过它指向整个数组。

例如一个指向有10个整型元素数组的指针定义为:

int (*p)[10]; // 由于[]的优先级高于*,所以必须添加()

注意:二维数组的 数组名是一个数组指针 ,若有:

int a[4][10]; int (*)p[10];  p = a; // a的类型是 int(*)[10]

对应关系如下图:

[C/C++基础] 1.数组

若有如下代码:

int a[10]; int (*p)[10] = &a; // 注意此处是&a,不是a,a的类型是int*,&a的类型是int(*)[10] int *q = a;

对应关系如下图:

[C/C++基础] 1.数组

可见,p与q虽然都指向数组的一个元素,但由于p的类型与q的类型不同,p是指向有10个元素整型数组的指针,*p的大小是40字节,故p+1跳过40字节;而q是指向整型的指针,*q的大小是4字节,故q+1跳过4字节。

1.3.3 指针在数组中的应用

指针和数组密切相关,特别是在表达式中使用数组名时,该名字会自动转换为指向数组首元素(第0元素)的指针。

int ia[] = {0, 2, 4, 6, 8}; int *ip = ia; // 指针ip指向了数组ia的首地址  ip = &ia[4]; // ip指向了ia[4]  ip = ia; // 重新指向ip[0] int *ip2 = ip + 4 // ip2指向ia[4]

注:对于int a[10],可以有以下几种方式表示a[1]:

&a[0] + 1;  // &a[0]等价于a,为首元素的地址 (int*)&a + 1; // &a为指向数组的指针,与a类型不同(&a类型为int(*)[10]),但指向的单元相同,强制转化为int*,加1跳过一个int大小 (int*)((char*)&a + sizeof(int)) ; // 强制转化为char*,加1跳过char的大小,跳了4次(int的大小), 再强制转化为int*(指向a[1]的指针类型为int*)

&、*总结:

例如这样的声明 int a[4][5];

&a : 类型为 int(*)[4][5];

a+i :类型为 int(*)[5];

*(a+i) :类型为 int*;

*(*(a+i) + j) :类型为int;

*(a+i) = a[i];

*(*(a+i) + j)  = *(a[i] + j) = a[i][j];

数组就暂时告一段落,最后在强调一点,在遇到指针问题的时候,首先要明确指针指向的是什么,才能知道跳跃的长度。

希望可以加深大家的理解~.~

正文到此结束
Loading...