0%

数组与指针


一维数组与指针

数组名代表的数组的首地址,通过这个首地址我们可以对这块内存区域进行访问,因为数组分配空间是按照顺序依次分配的。

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

std::cout << "show array a : ";
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
std::cout << a[i] << "\t";
}
std::cout << std::endl;

输出:

1
show array a : 1    2   3   4   5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int a[] = {1, 2, 3, 4, 5};

PRINT_INFO("a = " << a); //->a[0]

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
PRINT_INFO("----------------------------");
PRINT_INFO("&a[" << i << "] = " << &a[i]); //->a[i]
PRINT_INFO("a[" << i << "] = " << a[i]); //a[i]
PRINT_INFO("a + " << i << " = " << a + i); //->a[i]
PRINT_INFO("*(a+" << i << ") = " << *(a + i));//a[i]
PRINT_INFO("----------------------------");
}

PRINT_INFO("&a = " << &a); //->int[]
PRINT_INFO("&a + 1 = " << &a + 1); //->int[] + 1

PRINT_INFO("(&a + 1) - &a = " << (char* )(&a + 1) - (char* )&a); //相差字节

输出:

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
26
27
28
29
30
31
32
33
34
[INFO]: a =      0x7fff1b9e6300
[INFO]: ----------------------------
[INFO]: &a[0] = 0x7fff1b9e6300
[INFO]: a[0] = 1
[INFO]: a + 0 = 0x7fff1b9e6300
[INFO]: *(a+0) = 1
[INFO]: ----------------------------
[INFO]: ----------------------------
[INFO]: &a[1] = 0x7fff1b9e6304
[INFO]: a[1] = 2
[INFO]: a + 1 = 0x7fff1b9e6304
[INFO]: *(a+1) = 2
[INFO]: ----------------------------
[INFO]: ----------------------------
[INFO]: &a[2] = 0x7fff1b9e6308
[INFO]: a[2] = 3
[INFO]: a + 2 = 0x7fff1b9e6308
[INFO]: *(a+2) = 3
[INFO]: ----------------------------
[INFO]: ----------------------------
[INFO]: &a[3] = 0x7fff1b9e630c
[INFO]: a[3] = 4
[INFO]: a + 3 = 0x7fff1b9e630c
[INFO]: *(a+3) = 4
[INFO]: ----------------------------
[INFO]: ----------------------------
[INFO]: &a[4] = 0x7fff1b9e6310
[INFO]: a[4] = 5
[INFO]: a + 4 = 0x7fff1b9e6310
[INFO]: *(a+4) = 5
[INFO]: ----------------------------
[INFO]: &a = 0x7fff1b9e6300
[INFO]: &a + 1 = 0x7fff1b9e6314
[INFO]: (&a + 1) - &a = 20

数组名 a 代表着数组的首地址,a[i] <==> *(a+i) 这个表达式是等价的,a+i 表示的是以内存地址a 移动了 i 个 int 的地址

a,&a[0],&a 这三个元素打印的地址是相同的,但是他们三个的意义却是有很大的不同。a 代表数组的首地址,在其是一维数组时,与 &a[0] 代表的意义一样,首地址。
&a 代表的是把数组看成一个整体取地址 &a + 1 则表示数组作为一个整体移动了一个元素(数组),其实相当于移动了 5 * 4 = 20 个字节

利用数组名,指针变量来访问数组元素:

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
26
27
28
29
30
31
32
33
34
int a[] = {1, 2, 3, 4, 5};
int *p = NULL;

/*
利用数组名访问数组
*/
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
PRINT_INFO("a[" << i << "] = " << a[i]);
}

PRINT_INFO("----------------------------");

/*
运用指针访问数组的元素,a + i 表示数组的其实地址开始偏移 sizeof(int) * i 个字节的地址,
取出这个地址的值: *(a + i)
*/
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
PRINT_INFO("*(a + " << i << ") = " << *(a + i));
}

PRINT_INFO("----------------------------");

/*
利用第一个元素,地址访问数组
*/
p = &a[0];

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
PRINT_INFO("*(p + " << i << ") = " << *(p + i));
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[INFO]: a[0] = 1
[INFO]: a[1] = 2
[INFO]: a[2] = 3
[INFO]: a[3] = 4
[INFO]: a[4] = 5
[INFO]: ----------------------------
[INFO]: *(a + 0) = 1
[INFO]: *(a + 1) = 2
[INFO]: *(a + 2) = 3
[INFO]: *(a + 3) = 4
[INFO]: *(a + 4) = 5
[INFO]: ----------------------------
[INFO]: *(p + 0) = 1
[INFO]: *(p + 1) = 2
[INFO]: *(p + 2) = 3
[INFO]: *(p + 3) = 4
[INFO]: *(p + 4) = 5

二维数组与指针

二维数组的各个地址的含义

数组名代表了数组的首地址,也代表了第一行的首地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a[][3] = {{1, 2, 3}, {4, 5, 6}};

PRINT_INFO("a = " << a); //->a[0][0]
PRINT_INFO("a + 1 = " << a + 1); //->a[1][0]
PRINT_INFO("a[0] = " << a[0]); //->a[0][0]
PRINT_INFO("a[1] = " << a[1]); //->a[1][0]
PRINT_INFO("a[0] + 1 = " << a[0] + 1); //->a[0][1]
PRINT_INFO("a[1] + 1 = " << a[1] + 1); //->a[1][1]
PRINT_INFO("&a[0] = " << &a[0]); //->a[0]
PRINT_INFO("&a[1] = " << &a[1]); //->a[1]
PRINT_INFO("&a[0] + 1 = " << &a[0] + 1);//->a[1]
PRINT_INFO("&a[1] + 1 = " << &a[1] + 1);//->a[2]
PRINT_INFO("&a[0][0] = " << &a[0][0]); //->a[0][0]
PRINT_INFO("&a = " << &a); //->a[][3]
PRINT_INFO("&a + 1 = " << &a + 1); //->a[][3] + 1

PRINT_INFO("(&a + 1) - &a = " << (char* )(&a + 1) - (char* )&a); //相差字节

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[INFO]: a =         0x7fff7d937ba0
[INFO]: a + 1 = 0x7fff7d937bac
[INFO]: a[0] = 0x7fff7d937ba0
[INFO]: a[1] = 0x7fff7d937bac
[INFO]: a[0] + 1 = 0x7fff7d937ba4
[INFO]: a[1] + 1 = 0x7fff7d937bb0
[INFO]: &a[0] = 0x7fff7d937ba0
[INFO]: &a[1] = 0x7fff7d937bac
[INFO]: &a[0] + 1 = 0x7fff7d937bac
[INFO]: &a[1] + 1 = 0x7fff7d937bb8
[INFO]: &a[0][0] = 0x7fff7d937ba0
[INFO]: &a = 0x7fff7d937ba0
[INFO]: &a + 1 = 0x7fff7d937bb8
[INFO]: (&a + 1) - &a = 24

通过上面的程序,可以知道的是 a 代表了数组的首地址,代表了第一行的首地址。记住了它这里与 &a[0][0] 值相等但是并没有代表他的含义。我们可以把二维数组这么认为,把每一行当成一个整体作为一个元素,所以有咱们的 a[0], a[1] 正如我们初始化里面的一样,把每行当成一个数组用 {} 。

指针数组

指针数组注意定语是数组,也就是说数组时核心,那么我们想想数组的话就会有元素的,那么比较特殊,它的元素不是我们前面学的整形、字符型,而是指针类型。也就是说元素是一个一个的地址。

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
26
27
28
29
30
31
32
33
34
35
36
37
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
int* p[2] = {NULL};

p[0] = a[0];
p[1] = a[1];

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("a[" << i << "][" << j << "] = " << a[i][j]);
PRINT_INFO("p[" << i << "][" << j << "] = " << p[i][j]);
}
}

PRINT_INFO("----------------------------");

/*
运用指针数组的形式访问
*/
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(p[" << i << "] + " << j << ") = " << *(p[i] + j) );
}
}

PRINT_INFO("----------------------------");

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(*(p + " << i << ") + " << j << ") = " << *(*(p + i) + j) ); //p[i] == *(p + i) //a[i] == *(a + i)
}
}

输出:

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
26
[INFO]: a[0][0] = 1
[INFO]: p[0][0] = 1
[INFO]: a[0][1] = 2
[INFO]: p[0][1] = 2
[INFO]: a[0][2] = 3
[INFO]: p[0][2] = 3
[INFO]: a[1][0] = 4
[INFO]: p[1][0] = 4
[INFO]: a[1][1] = 5
[INFO]: p[1][1] = 5
[INFO]: a[1][2] = 6
[INFO]: p[1][2] = 6
[INFO]: ----------------------------
[INFO]: *(p[0] + 0) = 1
[INFO]: *(p[0] + 1) = 2
[INFO]: *(p[0] + 2) = 3
[INFO]: *(p[1] + 0) = 4
[INFO]: *(p[1] + 1) = 5
[INFO]: *(p[1] + 2) = 6
[INFO]: ----------------------------
[INFO]: *(*(p + 0) + 0) = 1
[INFO]: *(*(p + 0) + 1) = 2
[INFO]: *(*(p + 0) + 2) = 3
[INFO]: *(*(p + 1) + 0) = 4
[INFO]: *(*(p + 1) + 1) = 5
[INFO]: *(*(p + 1) + 2) = 6

以上三种方式情况都能访问到我们的二维数组。

首先我们定义的指针数组,那么数组的元素只能是指针,同时我们上面进行了分析, 将二维数组的每行看成是一个一维数组,那么 a[0], a[1] 就是每行的首地址。那么我们初始化指针数组就顺理成章了。此时数组 a 里面就有两个元素 a[0] 和 a[1]。
p[0] == a[0] p[1] == a[1] ,知道每行的首地址移动 j 个元素,实际偏移的地址量 sizeof(类型) * j 个字节。那么知道了地址取出这个单元个的值,加 * 就 ok 了,我们知道带有中括号的表达式可以这么写 p[i] == *(p + i)。

数组指针

定语是指针,那么作为指针就应该有指向了,只不过它指向的是一个数组,一个什么样的数组呢,它指向一个包含 N 个元素的一维数组。原型如:int (*p) [N];

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 a[][3] = {{1, 2, 3}, {4, 5, 6}};

/*定义数组指针*/
int (*p) [3] = NULL;

p = a; //行指针

/*
运用指针数组的形式访问
*/
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(p[" << i << "] + " << j << ") = " << *(p[i] + j) );
}
}

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(*(p + " << i << ") + " << j << ") = " << *(*(p + i) + j) ); //p[i] == *(p + i) //a[i] == *(a + i)
}
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
[INFO]: *(p[0] + 0) = 1
[INFO]: *(p[0] + 1) = 2
[INFO]: *(p[0] + 2) = 3
[INFO]: *(p[1] + 0) = 4
[INFO]: *(p[1] + 1) = 5
[INFO]: *(p[1] + 2) = 6
[INFO]: *(*(p + 0) + 0) = 1
[INFO]: *(*(p + 0) + 1) = 2
[INFO]: *(*(p + 0) + 2) = 3
[INFO]: *(*(p + 1) + 0) = 4
[INFO]: *(*(p + 1) + 1) = 5
[INFO]: *(*(p + 1) + 2) = 6

大家要注意的是类型的是为指针变量赋值的时候,类型要匹配。
二维数组 a 代表着数组名,同时也是第一行的首地址,我们说把每个一维数组当成一个整体,那么我们的二维数组就成了一个一维数组了,一维数组怎么取元素的呢? a[0], a[1] … a[i] 就是这个二维数组中每一个一维数组的值,但是切记它也只是一个地址。所以取整个数组的元素的值就有了上面的表述方式了。 *(a[i] + j) ,*(*(a + i) + j), i 表示行指针 a 移动的行数,取出它的值是一行的首地址, j 表示在此行的基础上移动的列数,最终得到的是这个数组第 i 行 j 列的地址了,取值的话就加上 * 就 ok 了。

数组指针,本质是一个指针,指向了一个数组,那么它里面存放的是一个地址。你可以把理解为一个二级指针。这样也是可以的。

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 a[][3] = {{1, 2, 3}, {4, 5, 6}};

/*定义数组指针*/
int (*p) [3] = NULL;

p = &a[0]; //行指针

/*
运用指针数组的形式访问
*/
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(p[" << i << "] + " << j << ") = " << *(p[i] + j) );
}
}

for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(*(p + " << i << ") + " << j << ") = " << *(*(p + i) + j) ); //p[i] == *(p + i) //a[i] == *(a + i)
}
}

数组指针数组

按照咱们的上面来分析,定语是数组,那么数组就有元素,修饰语就是数组指针,那么我们可以这么认为它的本质是一个数组,数组包含了若干个元素,每个元素都是一个数组指针,也就是每一个元素是一个指针,指向了一个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*数组指针数组 ,每个元素都是一个数组指针*/
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
int b[][3] = {{7, 8, 9}, {10, 11, 12}};

int (* p[]) [3] = {a, b};

//PRINT_INFO(sizeof(p) / sizeof(p[0]));

for (int k = 0; k < sizeof(p) / sizeof(p[0]); ++k)
{
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 3; ++j)
{
PRINT_INFO("*(*(p[" << k << "] + " << i << ") + " << j << ") = " << *(*(p[k] + i) + j) );
}
}
PRINT_INFO("------------------------");
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[INFO]: *(*(p[0] + 0) + 0) = 1
[INFO]: *(*(p[0] + 0) + 1) = 2
[INFO]: *(*(p[0] + 0) + 2) = 3
[INFO]: *(*(p[0] + 1) + 0) = 4
[INFO]: *(*(p[0] + 1) + 1) = 5
[INFO]: *(*(p[0] + 1) + 2) = 6
[INFO]: ------------------------
[INFO]: *(*(p[1] + 0) + 0) = 7
[INFO]: *(*(p[1] + 0) + 1) = 8
[INFO]: *(*(p[1] + 0) + 2) = 9
[INFO]: *(*(p[1] + 1) + 0) = 10
[INFO]: *(*(p[1] + 1) + 1) = 11
[INFO]: *(*(p[1] + 1) + 2) = 12
[INFO]: ------------------------