最近在学习c语言数组和指针的时候卡了很久,有一个问题始终想不通:
//定义一个二维数组
int arr[ROWS][CLOS];
数组可以以下方式进行遍历:
//数组赋值...略
int newline = 1;
for (int* p = &arr[0][0]; p < &arr[ROWS-1][CLOS]; p++)
{
printf("%d ", *p);
if (newline++ % 10 == 0 )
{
printf("\n");
}
}
这个书上的解释是:c语言按照行主序来存储二维数组;换句话说就是先是存储第0行,再是第q一行…
如图:
所以可以按照一维数组的方式对数组进行遍历。
于是我想到了另一种方法:
for ( int *p = arr; p < arr + ROWS*CLOS; p++)
{
printf("%d",*p);
}
按照理解二维数组arr打印时可以看成一个以为数组,那么arr+1就是下一个元素的位置,一共有ROWS*CLOS个元素,所以这样打印是没有问题的。
但是打印出的结果是把arr里的数据打印完过后,还打印了很多个不知道哪里来的超级长的数值。这个说明:
我对指针加减并没有理解正确,指针加一确实是指向了下一个数字的位置,但是这是个二维数组,别忘了arr[0] 和&arr[0][0]的意义是不一样的(虽然它们的数值是一样的)它们的类型是不同的,类型不同时指针加一会有很大的区别。
于是做了以下实验—打印数组的各种地址:
实验一:
printf("-----------------------\n");
printf("arr :%p\n", arr);
printf("arr+1:%p\n", arr+1);
printf("arr+2:%p\n", arr+2);
printf("-----------------------\n");
printf("arr[0]:%p\n", &arr[0]);
printf("arr[1]:%p\n", &arr[1]);
printf("arr[2]:%p\n", &arr[2]);
printf("arr[0]+1:%p\n", arr[0]+1);
printf("arr[0]+2:%p\n", arr[0]+2);
printf("-----------------------\n");
printf("arr[0][0]: %p\n", &arr[0][0]);
printf("arr[0][1]:%p\n", &arr[0][1]);
-----------------------
arr :000000D0EBDEF690
arr+1:000000D0EBDEF6B8
arr+2:000000D0EBDEF6E0
-----------------------
arr[0]:000000D0EBDEF690
arr[1]:000000D0EBDEF6B8
arr[2]:000000D0EBDEF6E0
arr[0]+1:000000D0EBDEF694
arr[0]+2:000000D0EBDEF698
-----------------------
arr[0][0]: 000000D0EBDEF690
arr[0][1]:000000D0EBDEF694
我这里ROWS == CLOS == 10,由于arr数组存放的是int型数值即4个字节,arr是一个二维数组,其实也是一个一维数组:arr[0]是一个数组 arr[1]是一个数组…arr的每个元素都是一个长度位ROWS的一维数组。
通过对比打印结果 有结论:
1.arr(二维数组的首地址)+1是一次加了40个字节,即每次+1都会跳到下一行的首地址(下一个一维数组元素的首地址)一次跳了4*ROWS个字节。
2.arr[0]每次加一增加了4个字节的地址,所以arr[0]也跳到了下一个元素,即arr[0]1]的位置, arr[0]的元素是int类型的值。
实验二:
printf("-----------------------\n");
int* p_arr = arr;
int* pp = &arr[0];
printf("arr :%p\n", arr);
printf("&arr[0] :%p\n", arr[0]);
printf("&arr[0][0]:%p\n", &arr[0][0]);
printf("-----------------------\n");
printf("pp :%p\n", pp);
printf("pp+1:%p\n", pp + 1);
printf("pp+2:%p\n", pp + 2);
printf("-----------------------\n");
printf("p_arr :%p\n", p_arr);
printf("p_arr+1:%p\n",p_arr+1);
printf("p_arr+2:%p\n",p_arr+2);
arr :000000D7C70FF590
&arr[0] :000000D7C70FF590
&arr[0][0]:000000D7C70FF590
-----------------------
pp :000000D7C70FF590
pp+1:000000D7C70FF594
pp+2:000000D7C70FF598
-----------------------
p_arr :000000D7C70FF590
p_arr+1:000000D7C70FF594
p_arr+2:000000D7C70FF598
这次可以得到如下结论:
1.arr 、&arr[0] 、&arr[0][0]三个指针的值是相同的。
2.指向arr的指针p_arr+1时并不是像arr+1一样一次加上ROWS4个字节的长度 ( 原因是:定义时p_arr的类型是int型的,所以编译器默认的将p_arr指向的内存看成是存放 int类型元素的内存块,所以p_arr虽然指向了arr但是+1时并不是加上一个一维数组大小的长度,如果要让p_arr+1一次增加ROWS4个字节长度 需要定义成:int(p_arr)[ROWS] 这代表了指针p_arr指向长度为ROWS的int数组,这样p_arr+1时会一次加上数组长度ROWS4个字节的长度 )。
实验三:
int (*p_arr)[ROWS] = arr;
printf("p_arr :%p\n", p_arr);
printf("p_arr+1:%p\n",p_arr+1);
printf("p_arr+2:%p\n",p_arr+2);
可以看到将p_arr指向nt ()[ROWS]类型时p_arr本身的类型是int()[ROWS],且p_arr+1 增加了ROWS 4个字节的长度。
结论:
1.实验二的第二个结论没问题。
实验四
将指向int类型的指针–指向char,这里把c赋值为26个小写字母:
char c[26];
int index = 97;
for (int i = 0; i < 26; i++)
{
c[i] = index++;
}
for (int i = 0; i < 26; i++)
{
printf("%c", c[i]);
}
int* p_c = c;
printf("%c", p_c + 1);
我们知道char类型一般占1个字节,int类型占4个字节,经过前三个实验可以推论处p_c+1时 由于p_c的指向的类型是int型,所以p_c会增加4个字节的位置,所以会打印第5个字母:e
abcdefghijklmnopqrstuvwxyz
----------------------
e
结论:
1.实验三的结论是对的。