微软的一道C语言笔试题(转)

来源:互联网 发布:100以内的质数c语言 编辑:程序博客网 时间:2024/04/28 09:41
 
 

 

题目:

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.ip[1]指向指针s.p*/

4 s.p = p;                     /*重新设置指针s.ps.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 = pp代表的是结构体的首地址,s.p是结构体内的执政变量,因此这一句就实现了将结构体的地址赋予了结构体的内部变量,我记得这种方法在VC++里好像见得较多,在C语言里不建议使用。

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

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

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

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

关于非法地址:

X86中将Int定义为16位,因此任何一个int类型的数据起始点都在偶数地址上,不如0x00000000~0x00000001时一个整体,而(5) 中将结构体的首址定义到了基数地址去了,就会报错,同样象ARM32位的存储结构,在不使用Thumb指令的时候,任何存储单元的起始地址的低3位必然为0,也就是从0开始每4个字节作为一个存储单元。

讨论:

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

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