指针和数组
C 语言中指针和数组这两个概念密不可分,以至于如果没有理解其中一个概念,就无法彻底理解另一个概念。
数组
很基础的一些知识点我就不重复了。关于数组,需要注意的有两个点:
- C 语言中只有一维数组,而且编译期间就要知道数组的精确大小。数组的元素可以是任何类型的对象,当然也包含了数组,这也就是多维数组。
- 对于一个数组,我们其实只能干两件事情:确定数组的大小、获取指向该数组下标为 0 的元素的指针。看到这话你是不是要反驳我了:不对呀,你写博客写糊涂了吧?我还可以
arr[1]
的操作呀。其实[]
操作的本质是通过指针来实现的。比如arr[i] = 1
其实上是*(arr + i) = 1
,这里的arr
就是指向该数组下标为 0 的元素的指针。
既然arr[0] = 1
本质上是*(arr + 0) = 1
,那么我们可以整个活:
1 | 0[arr] = 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
38
39
int main() {
int rows = 3; // 行数
int cols = 4; // 列数
int arr[rows][cols];
// 初始化二维数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = i * cols + j;
}
}
// 验证地址连续性
int *ptr = &arr[0][0];
int isContiguous = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// 计算下一个元素的地址
int *nextPtr = &arr[i][j];
printf("%p\n",&arr[i][j]);
if (nextPtr != ptr) {
isContiguous = 0;
break;
}
ptr++;
}
}
if (isContiguous) {
printf("二维数组的地址是连续的。\n");
} else {
printf("二维数组的地址不是连续的。\n");
}
return 0;
}运行结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
130x7fff2f05bb80
0x7fff2f05bb84
0x7fff2f05bb88
0x7fff2f05bb8c
0x7fff2f05bb90
0x7fff2f05bb94
0x7fff2f05bb98
0x7fff2f05bb9c
0x7fff2f05bba0
0x7fff2f05bba4
0x7fff2f05bba8
0x7fff2f05bbac
二维数组的地址是连续的。第二种情况:使用malloc来分配一个二维数组
1 |
|
运行结果如下:
1 | 0x55b2818142c0 |
通过 malloc
分配的二维数组的地址在内存中通常是不连续的。malloc
函数分配的内存块是堆内存,这些内存块通常在堆中的不同位置。因此,二维数组的各行和各列在内存中可能是分散的,不一定是连续的。
当你使用 malloc
分配一个二维数组时,你实际上在堆中创建了一个指向指针的数组,其中每个指针指向一个独立的内存块(一维数组),这些内存块存储了实际的数据。因此,二维数组的不同行在堆中的不同位置,它们不是物理上连续的内存块。
如果你需要连续的内存块以便于优化访问或传递给函数,你可以使用一维数组来模拟二维数组,并在计算索引时手动进行转换,以使其在一维数组中是连续的。这将涉及到一些数学运算,但可以帮助你实现连续内存存储。
如果你需要大规模的多维数组,并且连续内存非常重要,你可能需要考虑使用静态数组或者专门设计的数据结构,而不是 malloc
动态分配的内存。