玩命加载中qwq

数组,数组,数组-续

  • 2017-06-26
  • 1,394
  • 0

在这篇续中我们先来解决第一篇中留下的两个问题,然后我们进一步发现数组的另一处秘密。

我们现在解决问题二。因为问题二牵扯到的东西比较简单。
问题二:在结构体

struct tag_abc {
int x;
int y[2];
} abc[2][2][2][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ,15, 16};

中16位于哪里?

我们先来看单个结构体内:有1个int名叫x。但是y包含两个int。一颗结构体也就包含3个int。那么从abc[0][0][0][0]开始初始化,则有

abc[0][0][0][0] == {1, 2, 3}
abc[0][0][0][1] == {4, 5, 6}
abc[0][0][1][0] == {7, 8, 9}
abc[0][0][1][1] == {10, 11, 12}
abc[0][1][0][0] == {13, 14, 15}
abc[0][1][0][1] == {16, 0, 0}

所以16是abc[0][1][0][1].x的值。你猜对了吗?:)

问题一:思考,将数组int a[10]的偶数位元素统一赋值成16。并且使用这种奇怪的寻址方式。a[0] = 16; a[2] = 16; a[4] = 16…
代码如下:

int a[10];
int i;
for (i = 0; i < 10; ++i)
{
	if (0 == i % 2)
		i[a] = 16;
	else
		a[i] = 0;
}

我们来思考一个问题,如果你来编译C语言(补注1)你怎样处理a[i]的同时又能处理i[a]?要解决这个问题,我们要回到我在文章开始不久提到的另一种下标的使用,那就是:

 a[i] == i[a] == *(a + i) == *(i + a) 

也就是说a其实是指向a[0]的指针,a的值等于(&a[0])。那么*(a + i) 就表示内存(&a[0] + i)地址处的内容———因为我们知道int指针加一,就把内存向后移动了1 * sizeof(int)个Byte。这样的话,a[i]会被翻译成*(a + i),同样,i[a]就被翻译为*(i + a)。

既然如此*(a – i)就等于a[-i],*(-i + a)就等于-i[a]?是不是这样我们马上来试试。

int a[10] = {1,2,3,4,5,6,7,8,9,0};
int i;
for (i = 0; i < 10; ++i)
	printf("%d\n", a[-i]);

但是很抱歉,上面代码很可能会导致程序崩溃。因为a的地址是a[0]的地址,从 &a[0] 处再往前搜索内存是不行的。&a[0]前的内存没有我们的程序声明,所以它有可能是操作系统保留的。访问这块区域的话OS会毫不留情地给出Access Violation(内存访问失败)错误。如果没有错误,程序也会遍历到乱码。如果我们定义一个指针,并且把它指向a[9],那么程序就走得通了:

int a[10] = {1,2,3,4,5,6,7,8,9,0};
int i;
int * pae = &a[9];
for (i = 0; i < 10; ++i)
	printf("%d\n", pae[-i]);

以上代码中pae是Pointer to Array A’s End 的缩写。以上程序的执行结果是:
0
9
8
7
6
5
4
3
2
1
当然,程序

int a[10] = {1,2,3,4,5,6,7,8,9,0};
int i;
int * pae = &a[9];
for (i = 0; i < 10; ++i)
	printf("%d\n", (-i)[pae]);

的执行结果也一样咯:P 再下面的程序很可能会出现乱码:

int a[10] = {1,2,3,4,5,6,7,8,9,0};
int i;
int * pae = &a[9];
for (i = 0; i < 10; ++i)
	printf("%d\n", -i[pae]);

因为[]下标运算符的优先级比单目前缀取负数的优先级高,-i[pae]实际上是-1 * i[pae]的结果。但是pae的地址是&a[9],在循环中i的值是递增的。所以程序遍历到了a[9],a[10]一直到a[18]。但是a[9]以后的内存区域又不是我们的程序所声明的,所以要么会出现乱码,要么会导致内存访问失败。

所以在使用下标的方式中a[i]如果i为正数,正向遍历数组。如果i是负数逆向遍历数组。

好啦,我们来总结一下在这两篇文章中所提到的内容:《数组,数组,数组》中上半部分介绍了一维数组的声明、定义及初始化方式。下半部分介绍了多维数组的声明、定义、初始化方式。而且说明了一个很重要的问题:在内存中,不管是几维的数组都是线性存放的。《数组,数组,数组——续》中介绍了数组遍历的机制。通过这个机制我们就能明白数组下标为负数时的含义。至此数组的内容也就差不多了。接下来就要靠学而时习之的习了。

补注1:学习C语言时,请大家时刻这样思考:如果我来编译C语言,我会XXX……这样思考的目的是增强对C语言的感觉。不仅可以在短时间内掌握C语言,而且能克服艾宾浩斯遗忘曲线的影响。这样即使长时间不再使用C语言也不会忘记怎样写C语言。背住C语言关键词和语法是枯燥而困难的。而从理解的层面上学习运用了形象记忆调用了多处大脑皮层来处理信息,会减轻记忆与遗忘带来的痛苦。

感谢打赏!
支付宝

灌水吐槽区(登录QQ有头像!)

还没有人吐槽呢~快来抢沙发吧

你必须 登录 才能发表灌水吐槽区(登录QQ有头像!).