Interview Q&A - 程序会在哪一行死掉?

来源:互联网 发布:3d地球仪软件 编辑:程序博客网 时间:2024/04/30 00:04
struct S{int i;int * p;};void main(){struct S s;int * p = &s.i; //(1)p[0] = 4; //(2)p[1] = 3; //(3)s.p = p; //(4)s.p[1] = 1; //(5)s.p[0] = 2; //(6)}

答案:程序执行最后一句 s.p[0] = 2 死掉。

分析:下面是逐条分析

struct S s;

(1) int * p = &s.i; /*取成员s.i的地址*/

(2) p[0] = 4; /*设置成员s.i为4。因为指针p指向i的地址,p[0]指向i*/

(3) p[1] = 3; /*设置成员s.p为3。因为p[0]指向s.i,p[1]指向指针s.p*/

(4) s.p = p; /*重新设置指针s.p为s.i的地址*/

(5) s.p[1] = 1; /*置s.p指针为1。因为前面s.p指向s.i的地址,所以s.p[1]指向s.p*/

(6) s.p[0] = 2; /*因为s.p指针已经通过前面被设置为1,即非法地址,所以s.p[0]想通过s.p去访问s.i显然是非法的。*/

----------------------------------------------------


(1) 要注意程序中定义了两个*p,一个是主程序的指针,一个是结构体中的指针变量。通过第一句,使得主程序指针获得了结构体的存储首地址;

(2) 注意第二句的用法,以前没见过,不常见,也许只有当指针指向数组或者结构体等类型的变量的时候,才可以如此使用。

一个变量有两个属性,变量名和变量值。例如“int a = 10”,那么a代表变量的名字,同时a这个变量名有代表了10这个值。指针变量同样如此,例如“int *p = &a”,那么p就代表了一个指针变量的变量名,而它的值呢?指针变量的变量值是一个地址,这个例子中就代表了a变量所存放的内存地址单元的地址。因此指针名代表的也是指针所指向对象的地址,那么如何理解第二句中的p[0]呢?p[0]所定义的是一个变量值还是一个地址值呢?

因为p[0]使用了数组的表示方法,我们再回到数组。例如“int a[]”,定义了一个数组,数组名a代表了数组的首地址,而a[0]呢代表什么呢?当然就是a数组中第一个变量的值了,所以第二句“p[0] = 4”也就好理解了,相当于结构体中i对应的内存单元的值为4。

(3) 这一句的理解同上;

(4) s.p = p。p代表的是结构体的首地址,s.p是结构体内的指针变量,因此这一句就实现了将结构体的地址赋予了结构体的内部变量,我记得这种方法在VC++里好像见得较多,在C语言里不建议使用。

这一句说明了另外一个重要的问题,结构体中第二个变量的值(指针变量的值)就是结构体的地址,这是理解下面两句的关键。

(5) s.p[1]=1。先来看看s.p[1]是什么?s.p是指针,代表了结构体的首址,s.p[0]则代表了结构体中第一个变量的值,于是s.p[1]代表的就是第二个变量的值,只不过第二个是指针变量,因此1代表的是一个地址。哪个的地址?当然就是结构体的地址,即s.p=1。

通过这一句话就实现了将结构体定位在了内存单元的0×00000001处。重要!!!

(6) s.p[0] = 2。这句话本来没有错,它是将结构体的第一个元素的值赋为2。真正的错误出现在上一句,但编译器会在这里报错。要理解这个问题,还得说说“非法地址”的概念!

关于非法地址:

X86中将Int定义为16位,因此任何一个int类型的数据起始点都在偶数地址上,而(5)中将结构体的首址定义到了基数地址去了,就会报错。

讨论:

1) 如果将(5)(6)两句颠倒,就不会报错了,但第(5)句的危险依然存在,如果继续对这个指针操作的话依然会报错;

2) 这个程序是否会报错与具体使用的CPU有关,如果是8位结构的,比如单片机的话,也许不会出错。