C/C++基础 — 指针与数组、字符串
本博文由 西北工业大学MOOC 总结而来,以备以后回顾。
此次二维数组为自己增加的内容,如有错误欢迎留言指正
1、一维数组和指针
1.1、一维数组的地址
数组由若干元素组成,每个元素都有相应的地址,通过取址运算符(&)可以得到每个元素的地址。
C++规定,数组名既代表数组本身,又代表整个数组的地址,还是数组首元素的地址。即a与a的第0个元素的地址&a[0]相同。例如,
数组名是一个指针常量,因为不能出现在左值和某些算术运算中,例如:
1.2、指向一维数组的指针变量
定义指针时,用数组名初始化指针,即可将指针指向一维数组。指针的指向类型应和数组元素类型一致。例如:
1.3、通过指针访问一维数组元素
由于数组元素的地址是规律性增加的,更具指针算术运算规则,可以利用指针及其运算来访问数组元素。
访问一维数组元素有以下四种方法:
2、二维数组和指针
2.1、二维数组的地址
假如有一个二维数组:
int a[3][4] = { { 1, 3, 5, 7}, { 9, 11, 13, 15}, { 17, 19, 21, 23}};
其中,a 是二维数组名。a 数组包含 3 行,即 3 个行元素:a[0],a[1],a[2]。每个行元素都可以看成含有 4 个元素的一维数组。而且 C 语言规定,a[0]、a[1]、a[2]分别是这三个一维数组的数组名。如下所示:
a[0]、a[1]、a[2] 既然是一维数组名,一维数组的数组名表示的就是数组第一个元素的地址,所以 a[0] 表示的就是元素 a[0][0] 的地址,即
a[0] == &a[0][0];
a[1] == &a[1][0];
a[2] == &a[2][0];
所以二维数组 a[M][N] 中,a[i] 表示的就是元素 a[i][0] 的地址,即
a[i] == &a[i][0];
则 a[i]+j 就表示元素 a[i][j] 的地址,即:
a[i]+j == &a[i][j];
同时由一维数组 a[i] 和 *(a+i) 等价,引申至二维有:
*(a+i)+j == &a[i][j];
二维数组的首地址和数组名
二维数组就是一维数组,二维数组 a[3][4] 就是有三个元素 a[0]、a[1]、a[2] 的一维数组,所以二维数组 a[3][4] 的第一个元素不是 a[0][0],而是 a[0] (第一行元素的地址),所以数组名 a 表示的不是元素 a[0][0] 的地址,而是 a[0] 的地址,即:
a == &a[0]; //数组名代表数组第一行元素a[0]的地址
而 a[0] 又是 a[0][0] 的地址,即:
a[0] == &a[0][0];
则二维数组名 a 和 元素名 a[0][0] 的关系是:
a == &(&a[0][0]); //数组名代表数组第一行元素a[0]的地址
二维数组的空间在内存中也是连续的地址。对于内存而言,没有维度而言,内存是一维的,内存里面不分行也不分列,元素都是按顺序一个一个往后排的,所以二维数组中的每一个元素在内存中的地址都是连续的。如下:
定义一个二维数组:
int a[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
或
int a[3][4] = { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} };
按行输出为:
1 2 3 4
5 6 7 8
9 10 11 12
其元素的地址输出为:
0X18FF18 0X18FF1C 0X18FF20 0X18FF24
0X18FF28 0X18FF2C 0X18FF30 0X18FF34
0X18FF38 0X18FF3C 0X18FF40 0X18FF44
可以看出二维数组元素的地址在内存中是连续存在的。
2.2、指向二维数组的指针变量
前面提到过,二维数组是由一行一行的一维数组组成的,如 a[0]、a[1],a[2];同时也是由一个一个的单独的元素组成的,如 a[0][0]、a[1][1]、a[2][2]。所以指向二维数组的变量可以有两种形式:
1)行指针:(常用)
行指针的定义如下,其指向的是二维数组的第0行。形式如下:
指针类型 (*指针名称)[数组列数] = 数组名/数组第0行元素地址;
int a[M][N] = { .......};
int (*p)[N] = a; //其中N是二维数组a[M][N]的列数, 是一个数字。
或 //**数组长度不能定义成变量
int (*p)[N] = &a[0];//a == &a[0];
//***************注意与指针数组作区别,int *p[4] 为指针数组,
// 其中包含四个int型指针,即p[0],p[1],p[2],p[3]都是int型指针,其中存储的都是地址值
2)列指针:
列指针指向二维数组的第0个元素,其在形式上与指向单一对象的指针没有区别。形式如下:
指针类型 *指针名称 = 数组第[0][0]元素;
int a[M][N] = { .......};
int *p = &a[0][0]; //指向二维数组第0行第0列的元素
注意这两种指针都是一个指针,应与指针数组做区别(由多个指针元素组成的数组)。且指向二维数组的两种指针不能弄混,是因为二维数组的组成既是由行元素组成,也是由单个元素组成。行指针只能指向数组的行(一般指向第0行),其定义必须包含数组的列长度;列指针只能指向某一个元素,其相当于一个指向单独对象的指针。
2.3、通过指针访问二维数组的元素
1)行指针:(常用)
定义一个二维数组和指向该数组的指针:
int a[3][4] = { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} };
int (*p)[4] = a; //将指针指向二维数组a的首地址
则有如下的取值方式:
*(*(p+i)+j) == a[i][j];
括号的运算优先级最高,所以最先运算 p+i,将指针指向第 i 行,再运算 * 号解引用得到第 i 行的首地址 &a[i],再运算 &a[i]+j,将指针指向第 i 行 第 j 列的元素的地址&a[i][j],再运算 * 号解引用得到元素的值 a[i][j]。
同理,还有其他取值方式:
*(*p) == *(a[0]) == *(&a[0][0]) == a[0][0]; //value = 1
*(*p+1) == *(a[0]+1) == *(&a[0][1]) == a[0][1];//value = 2
*(*(p+1)) == *(a[0+1]) == *(&a[1][0]) == a[1][0];//value = 5
*(*(p+1)+2) == *(a[1]+2) == *(&a[1][2]) == a[1][2];//value = 7
2)列指针:
列指针的实质是一个指向单独元素的指针,利用二维数组在存储空间上的连续性,也可对通过相应操作取得相应的值。举例如下:
# include <stdio.h>
int main(void)
{
int a[3][4] = { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} };
int i, j;
int *p = &a[0][0]; //把a[0][0]的地址赋给指针变量p
for (i=0; i<3; ++i)
{
for (j=0; j<4; ++j)
{
printf("%-2d\x20", *(p+i*4+j));//由于存储空间是一维的,所以只能用i*4表示换行
}
printf("\n");
}
return 0;
}
3、字符串和指针(char型指针,c语言描述)
字符串指针的操作和数组指针的操作是相同的,所以本文在此也加入字符串指针的相关内容。
可以利用一个字符型的指针处理字符串,其过程与通过指针访问数组元素相同。使用指针可以简化字符串的处理,是处理字符串常用的方法。
3.1、字符串指针的定义
c++允许定义一个字符指针,初始化时指向一个字符串常量,一般形式为:
(*推荐)
char str[20] = "C Language";
char *p_str = str; //指针指向字符串数组的首地址
或定义时直接赋值
char *p_str = "C Language";
或
char *p_str;
*p_str = "C Language"
以上的方法定义和初始化的指针,都是指向字符串的首地址。
3.2、通过指针访问字符串
1)基本访问形式
2)通过字符指针遍历字符串
一般使用while循环来遍历字符串,形式有两种:
①while (*p != ‘\0’){ 操作内容 };
②while (*p){ 操作内容 };
3.3、指针访问二维字符串
二维字符串在形式上和二维数组相同,其操作方式也与指向二维数组的指针相同,只是在每一个字符串的末尾都以 ‘\0’ 结束,在使用时需要加以注意,多用判断方式越界操作。如图所示: