西西河

主题:【求助】向各位高手请教一个c语言中数组与指针的问题 -- 数值分析

共:💬84 🌺26 新:
分页树展主题 · 全看
/ 6
下页 末页
  • 家园 【求助】向各位高手请教一个c语言中数组与指针的问题

    最近遇到一个c语言中数组与指针的问题,看到版中高手云集,望各位能不吝指教一二。

    是这样的,以前学过c语言,好久不用了,最近作一个小程序。我一直认为二维数组和二阶指针是一回事儿(看来是基础没打牢啊,惭愧,惭愧。)但最近才发现问题。

    举个例子来说吧,假如我定义一个函数f1

    void f1(int **x)

    {

    ......

    }

    再在main函数中定义一个int型二维数组

    int a[5][5];

    然后用循环对a赋初值

    然后把它传到函数f1中,

    f1((int **)a);

    问题来了,在f1中无法正确访问数组。

    可是我把地址值打印出来,main函数中a的值与f1中x的值是一样的,(比如都是0x12345678) 但f1中x[0][0]与main中a[0][0]却不一样。

    为什么对同一个地址解引用,结果会不同呢?

    求教诸位,

    先行谢过。

    • 家园 这个问题实在很有趣

      数组名是什么?这是关键。这个东东不是指针,虽然有的书上说它是常数指针(const pointer),但叫它指针只能把读者彻底搞糊涂。见过一个指针满足 &p=p=*p 吗?要想真正理解多维数组,请运行下面的代码。

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

      int main()

      {

      int a[2][2], b[3][4][5];

      a[0][0]=1;

      b[0][0][0]=99;

      // 2-dim example

      printf("%d,%d,%d\n",a, (int *)a, (int **)a);

      printf("%d,%d,%d\n",*a, *((int *)a), *((int **)a));

      printf("%d,%d,%d\n",*a+1, *((int *)a)+1, *((int **)a)+1);

      printf("%d,%d,%d,%d\n", &a,a,*a,**a); //Same output for &a, a, *a.

      printf("%d,%d,%d,%d\n", &a+1,a+1,*a+1,**a+1); //Note the difference.

      // 3-dim example

      printf("%d,%d,%d,%d\n",b, (int *)b, (int **)b, (int ***)b);

      printf("%d,%d,%d,%d\n",*b, *((int *)b), *((int **)b), *((int ***)b));

      printf("%d,%d,%d,%d\n",*b+1, *((int *)b)+1, *((int **)b)+1, *((int ***)b)+1);

      printf("%d,%d,%d,%d,%d\n", &b, b, *b, **b, ***b); //Same output for &b, b, *b, **b.

      printf("%d,%d,%d,%d,%d\n", &b+1, b+1, *b+1, **b+1, ***b+1); //Note the difference.

      }


      本帖一共被 2 帖 引用 (帖内工具实现)
      • 家园 不是指针是什么呢?

        它肯定不是int*, 也不是int ** ... 

        它是什么呢?

        就是它自己吧...

        printf -> a, *a, **a, 都没问题...

        测试中....

      • 家园 顺便说个有趣的事儿

        大家讨论的很辛苦,顺便说个有趣的事儿,活跃下气氛

        按规范 a[b]等同于*(a+b),因为加法可交换*(a+b)=*(b+a),所以假设int a[5];则 a[4]和4[a]是等同的。

        我code中a[1][2][3]这样的都写成[3[2[1[a]]],编译绝对可通过,这样让读代码的人感到十分高深,呵呵。

        这招还可以用到出面试题中,看看受试者对c规范理解如何

        • 家园 高手...

          你搞编译器的?

          你上面少了个]吧?

          我对两维的试了试...编译通不过...VC... 测试中...

        • 家园 问题是一般人不用搞这么细的规范吧...

          这招还可以用到出面试题中,看看受试者对c规范理解如何

          也就知道怎么用就够了...

          除非搞编译器...

          • 家园

            熊兄说得对。

            不过面试时如果实在需要把对方刷下去,就用得到了。

            我还真碰到过这种事儿,老板居然亲自到我办公室,跟我说,待会儿会安排我面试一个人,让我找辙给拒了。

            类似的问题问了几个,对方就知难而退了。

            • 家园 对方没骂你BT? 

              • 家园 唉,我也不想啊

                唉,我也不想啊

                但当时不是你死就是我亡。。。没办法了。

                至于领导出于什么原因这么做,咱是实在猜不着啊

        • 家园 你牛,这种秘密都被你发现了

          这么好玩的东东我要收藏。

      • 家园 透彻

        透彻,真乃真知灼见

    • 家园 我对指针的理解是这样的

      其实指针这东西,只需要搞清两件事情:

      1.指针就是个很普通的变量,只不过它里面放的不是具体的数据,而是一个地址。您要是*它,它就按着自己记下的地址去提货。

      2.指针知道自己的“基类型”是个什么类型。它永远认为自己记的那个地址里放的就是基类型的数据。这点很重要,因为:

      2.1 其实指针里放的只是一个起始地址,您*它的时候,它就按照自己的“基类型”的长度从它记的地址开始去取那么长的内存数据。并且将这些二进制按“基类型”的方式解读:这么多位0和1到底记录的是一个整数,还是一个数组,还是一个浮点数。

      2.2 您要给指针+n,它就认为是要它指向后面第n个“基类型”数据。所以它会把自己记的地址加上个

      n*基类型长度

      这个第一点,只要是使用指针的都知道,没啥可说的。所以我们要用一个指针的时候,需要注意的就是要时刻记得这个指针的“基类型”是什么,这个影响到指针的大部分行为特点。

      数组和指针,就一句话。数组名是一个“基类型”是数组元素类型的指针常量。可以向指针一样用,但不能改变其取值。

      多维数组也是一句话。多维数组是“数组的数组”。

      其实我认为C语言里的指针和数组就这么点儿东西。

      希望对您有帮助。理解不对的地方也请大牛们指正。

    • 家园 你搞错了多维数组的概念。

      在c里,默认多维数组结构,实际上一通过一个内存片实现的。a[][],指向一个内存片,a指向首地址,从首地址开始,里面顺序存着a[0][0],a[0][1].....a[1][0]a[1][1]...等等。。

      int**类型指向一个地址类型,which指向一个int。

      所以a[][]定义里a是个指向int的指针,而不是指向一个int指针的指针。

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

      另外一种常用的动态实现多维数组的方法是这样子的。

      假如要实现一个5*5的二维数组,

      int ** a = (int **) malloc(5*sizeof(int *));

      for (int i = 0;i<5;i++) {

      a[i] = (int *) malloc (5*sizeof(int));

      }

      之后就可以用a[1][2]的形式访问多维数组,传递的时候传递int**类型。但要注意越界和释放问题。

      • 家园 你这个分配法有问题

        这个分配法有问题,数组a的地址不连续

        我修改一下,分配nx*ny的二维数组:

        int *a=(int**)malloc(nx*sizeof(int*));

        a[0]=(int*)malloc(nx*ny*sizeof(int));

        for (int i=1;i<nx;i++)

        {

        a[i]=a[0]+ny*i;

        }

        释放时

        free(a[0]);

        free(a);

        这样的好处是,二维数组的地址是连续的

        比如你要初始化,就可以

        memset(a[0],'\0',nx*ny*sizeof(int));

        原来你那种分配法就不行。

        再比如

        *(a[0]+m*ny+n)等于a[m][n],原来的分配法就不行

分页树展主题 · 全看
/ 6
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河