博主上大二了,接触linux自我认为还是一个有小小追求的人,觉得一直漂浮在上层没有根基,于是还是想看看linux内核,便重新看是看pointers on c扎实c语言基础。不再急功近利,不再认为看完书就学到了知识,实践出真知,自己动手才是王道。不多说,就用一个以前我写过的例子嘲讽以前的我吧!
还是一样的,pointers on c的chapter8的第五个编程小题。
函数原型
void matrix_multiply ( int *m1 , int *m2 ,int *r , int x , int y , int z ); //其中m1是第一个数组,m2是第二个数组,m3是输出数组x,y是m1数组的行列号,y,z是m2的行列号
先看第一遍的
void matrix_multiply ( int *m1 , int *m2 , int *r , int x , int y , int z ) { int i = 0; int j = 0; int k = 0; for ( i = 0 ; i < x ; i++ ) { for ( k = 0 ; k < z ; k++ ) { for ( j = 0 ; j < y ; j++ ) { *(*(&r+i)+k) += (*(*(&m1+i)+j)) * (*(*(&m2+j)+k)); } } } }
算法很简单,我的思路再反复确定之后认为没有错误,但是编译执行之后一直会出现,segmentation fault (core dumped)也就是传说中的数组越界。想了一下才明白问题的所在——r,m1,m2不是数组名!
假设有数组名a,那么&a会是当前的地址,并且假设a是一维数组,那么&a就是从列指针转化为行指针,具体的实例可以假设int a[10];只需要printf一下a+1的地址和&a+1的地址结果就显而易见!但是在这个题目中,不会将m1,m2,r当做数组名的,即使你将函数原型改为int m1[]这样的也可以,这里会当做是变量来处理。由于这里函数原型中的形参是*m1,*m2,*r这样的列指针,我想当然的认为这里可以转化行指针来处理,但实际上并不可以,因为这里是在堆栈中的变量。
那么问题究竟出在哪里那?
参考一个例子
firstfile.c:
int a[10];
int *b=a;
...
secondfile.c:
extern int *a;
extern int b[];
...
那么a[3]和b[3]的值会是什么那?这里由于第二个文件中会把a当做指针变量处理,取出指针变量位置中存储的值,再加上3*sizeof(int)得到一个值,再对得到的值解引用,取出那个位置中的值,而实际上a是数组名,那么最后的结果就是取出(a[0]+12)这个地址的值。(这个访问一般情况下是非法的)
而这里会将b当做是数组名,直接将b的地址加上12再解引用取值,这里也会是非法访问的。
其实仔细想想,就是一个根本区别——变量会创建空间,而数组名不会!
经过以上的思想挣扎之后我发现这里只能用列指针来进行操作,所以第二遍我就是对的了,嘿嘿!
void matrix_multiply ( int *m1 , int *m2 , int *r , int x , int y , int z ) { int i = 0; int j = 0; int k = 0; for ( i = 0 ; i < x ; i++ ) { for ( k = 0 ; k < z ; k++ ) { for ( j = 0 ; j < y ; j++ ) { *(r+i*z+k) += *(m1+i*y+j) * *(m2+j*z+k); } } } }
虽然只是改了一行,但是我还是感觉蛮好的!