#include <stdio.h>
int main(){
int a[3][3][3][3] = {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, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81};
for(int i1=0;i1<3;i1++){
for(int i2=0;i2<3;i2++){
for(int i3=0;i3<3;i3++){
for(int i4=0;i4<3;i4++){
printf("%p a[%d][%d][%d][%d] %d\n",&a[i1][i2][i3][i4],i1,i2,i3,i4,a[i1][i2][i3][i4]);
}
}
}
}
return 0;
}
0x16f877614 a[0][0][0][0] 1
0x16f877618 a[0][0][0][1] 2
0x16f87761c a[0][0][0][2] 3
0x16f877620 a[0][0][1][0] 4
0x16f877624 a[0][0][1][1] 5
0x16f877628 a[0][0][1][2] 6
0x16f87762c a[0][0][2][0] 7
0x16f877630 a[0][0][2][1] 8
0x16f877634 a[0][0][2][2] 9
0x16f877638 a[0][1][0][0] 10
0x16f87763c a[0][1][0][1] 11
0x16f877640 a[0][1][0][2] 12
0x16f877644 a[0][1][1][0] 13
0x16f877648 a[0][1][1][1] 14
0x16f87764c a[0][1][1][2] 15
0x16f877650 a[0][1][2][0] 16
0x16f877654 a[0][1][2][1] 17
0x16f877658 a[0][1][2][2] 18
0x16f87765c a[0][2][0][0] 19
0x16f877660 a[0][2][0][1] 20
0x16f877664 a[0][2][0][2] 21
0x16f877668 a[0][2][1][0] 22
0x16f87766c a[0][2][1][1] 23
0x16f877670 a[0][2][1][2] 24
0x16f877674 a[0][2][2][0] 25
0x16f877678 a[0][2][2][1] 26
0x16f87767c a[0][2][2][2] 27
0x16f877680 a[1][0][0][0] 28........
上面这组输出告诉了我们高维数组的变量地址是怎样变化的。arr[1][0]
的前一个地址是arr[0][n]
(n的意思是最后一维也就是arr[0][0][0]
这一层的时候,它的最后一个数据arr[0][0][0][n]
的后一个实际上是arr[0][0][1][0]
).
那么就是说,每一层维度的数组的最后一个元素的后继元素(如果还在数组里面的话),是下一层数组的第一个元素。
这些也其实说明了,在C内存中,所有的数组其实都是,无论是否高维,在内存中都是一维数组。
这是一个多维数组的指针
#include <stdio.h>
#define SIZE 4
int main(){
int arr[SIZE][SIZE][SIZE] = {{{1,2,3,4},
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}},
{{1,2,3,4},
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}},
{{1,2,3,4},
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}},
{{1,2,3,4},
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}}};
int (*p1)[4][4]=arr;
printf("arr[0]=%p, arr[1]=%p, arr[2]=%p, arr[3]=%p\n",arr[0],arr[1],arr[2],arr[3]);
printf("p1=%p, p1+1=%p, p1+2=%p, p1+3=%p\n",p1,p1+1,p1+2,p1+3);
printf("*p1=%p, (*p1)+1=%p, (*p1)+2=%p, (*p1)+3=%p\n",*p1,(*p1)+1,(*p1)+2,(*p1)+3);
printf(" %p %p %p %p",arr[0][0],arr[0][1],arr[0][2],arr[0][3]);
}
arr[0]=0x16b4db658, arr[1]=0x16b4db698, arr[2]=0x16b4db6d8, arr[3]=0x16b4db718
p1=0x16b4db658, p1+1=0x16b4db698, p1+2=0x16b4db6d8, p1+3=0x16b4db718
*p1=0x16b4db658, (*p1)+1=0x16b4db668, (*p1)+2=0x16b4db678, (*p1)+3=0x16b4db688
0x16b4db658 0x16b4db668 0x16b4db678 0x16b4db688
首先可以看到使用int (*p1)[4][4]=arr
指向了这个三位数组的第一层,也就是说这个指针所指向的了arr[0], 并且此时对p1使用加法的话,那么p1+1 就相当于arr[1]。可以从上面的输出中看出这一点。
然后之所以要使用int (*p1)[4][4]=arr
这样的方式去定义是为了告诉编译器p1这个指针在进行比如加法,减法之类的操作的时候,应该移动几个自己大小,在这里可以看出它移动了64,因为arr[1]与arr[0]之间相差了64个字节,更加细节的讲,就是arr[1]这个二维数组中储存了16个int数据,每个int类型数据占4个字节所以占用了
16
∗
4
=
64
16*4=64
16∗4=64个比特。
下面是一些骚操作
假设arr[4][4]
arr 相当于&arr[0]
arr+2 相当于&arr[2]
*(arr+2) 相当于 arr[2] 相当于 &arr[2][0]
*(arr+2)+1相当于 &arr[2][1]
*(*(arr+2)+1) 相当于arr[2][1]
下面是示例
#include <stdio.h>
int main(){
int zippo[4][2] = {{2,4},{1,3},{6,8},{5,7}};
int(*pi)[2] = zippo;
printf("pi=%p zippo=%p\n\n",pi,zippo);
printf("*(pi+2)=%p,*(pi+2)+1=%p\n",*(pi+2),*(pi+2)+1);
printf("%p %p\n\n",&zippo[2][0],&zippo[2][1]);
printf("*(*(pi+2))=%d,*(*(pi+2)+1)=%d\n",*(*(pi+2)),*(*(pi+2)+1));
printf("%d %d\n",zippo[2][0],zippo[2][1]);
}
pi=0x16f593740 zippo=0x16f593740
*(pi+2)=0x16f593750,*(pi+2)+1=0x16f593754
0x16f593750 0x16f593754
*(*(pi+2))=6,*(*(pi+2)+1)=8
6 8
下面这个历史是关于一个指向int的指针和一个指向指向的指针
#include <stdio.h>
int main(){
int zippo[3][2] = {{1,2},{3,4},{5,6}};
int a=2;
int *p1 = &a;
printf("%p\n",p1);
int **p2 = &p1;
*p2 = zippo[0];
printf("%d\n",**p2); //**p2 == zippo[0][0]
printf("p1=%p,zippo[0]=%p\n",p1,zippo[0]);
}
0x16d2ff74c
1
p1=0x16d2ff750,zippo[0]=0x16d2ff750
可以看到*p2实际上是p1的内存的内容,现在修改有*p2 = zippo[0]
相当于p1=zippo[0]
.因此p1的指向发生了改变
void somefunction(int arr[][n])
这样声明的函数说明,它的形参二维矩阵,且它的的内存的那个一维数组有n个int 类型。
当然第一个中括号中也可以填元素,但是编译器会忽。填上的好处是有助于告诉阅读代码的人,这里的输入的要求。
第一个方括号用于告诉编译器这里是一个指针,后面的方括号及其内容是在告诉编译器这个指针的类型。
复合字面量
#include <stdio.h>
int main(){
int *ptl1;
ptl1 = (int [2]){10,20}; //这是一个复合字面量(int [2]){10,20}
printf("%p",ptl1);
printf("\n%d, %d, %d",*ptl1,*(ptl1+1),ptl1[1]);
}
0x16f3cf760
10, 20, 20
之所以可以用ptl1[1]是因为数组实际上就是指针。在这里你可以理解为ptl1相当于一个数组,其指向了int类型,所以也是指针。