分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
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位结构的,比如单片机的话,也许不会出错。