【问题标题】:"Direction" of bidimensional arrays in CC中二维数组的“方向”
【发布时间】:2009-05-01 19:45:39
【问题描述】:

作为一个 C 新手,我对二维数组有点困惑。

如果我想表示一个3行5列的矩阵,我猜正确的声明是:

char a[3][5];

那么,这是一个包含 3 个指向 5 个字符数组的指针的数组还是什么?

为什么每当我尝试像下面这样循环遍历它时,它似乎读取了错误的结果?

int x, y;
for( x=0; x<3; x++ ){
    for( y=0; y<3; y++ ){
        printf( "%c", a[x][y] );
    }
}

以下是等价且正确的初始化方式吗?

char a[3][5] = {
               {1,0,0,0,1},
               {1,0,0,0,1},
               {1,0,0,0,1},
               };

char a[3][5] = {1,0,0,0,1,
                1,0,0,0,1,
                1,0,0,0,1};

感谢您最终的解释。


编辑

对不起,错别字,代码没有复制。顺便说一句,我一直让它们像垂直阅读一样阅读,而不是水平阅读。

同样在本教程http://www.cplusplus.com/doc/tutorial/arrays/ 的示例中,它以一种对我来说并不直接的方式读取数组,因为它似乎适用于 5x3、高度宽度、yx、 colsrows 结构,而不是 3x5、WidthHeight、xy。行第一列:

#define WIDTH 5
#define HEIGHT 3

int jimmy [HEIGHT][WIDTH];
int n,m;

int main ()
{
  for (n=0;n<HEIGHT;n++)
    for (m=0;m<WIDTH;m++)
    {
      jimmy[n][m]=(n+1)*(m+1);
    }
  return 0;
}

【问题讨论】:

  • 您的嵌套 fors 应该在内循环中使用 y...这可能会解释错误的结果;]
  • 顺便说一下,初始化数组的两种方法都是正确的。第二个使用大括号省略-但这当然是允许的。第一个使用尾随逗号,但也允许使用。就个人而言,我最喜欢带大括号且不带逗号的版本 - 但这只是我的口味。

标签: c arrays


【解决方案1】:

只是为了它是什么,什么不是。

char a[3][5];

不涉及指针。像这样的多维数组是......数组的数组,依此类推。在您的情况下,您有一个由 3 个 5 个字符组成的数组。当您使用 typedef 时,它会变得更加清晰。

typedef char T[5];
T a[3];

不涉及任何指针。如果你想访问这 3 个数组中的第一个数组,你可以这样做:

a[0]; 

它会返回一个char[5] 类型的对象。通常,您不会注意到这一点,因为通常您索引所有维度。所以a[0] 返回的数组是下一个索引的下标,例如a[0][1][1] 将应用于由a[0] 返回的数组,正如我们之前计算的那样,它的类型为char[5]

那么,这是一个包含 3 个指向 5 个字符数组的指针的数组还是什么?

让我们创建那个类型,看看它与上面的有什么不同。创建它很简单,只要你获得了基本的声明符:

  • 创建指针:*D
  • 创建数组:D[N]

D 只是一个现有的另一个声明符。所以现在让我们继续吧。首先你说array of 3 pointers to 5 arrays of chars...。我想你的意思是array of 3 pointers to arrays of 5 chars。首先,array of 5 的创建方式类似于

D1[5]

现在,让我们将 D1 替换为 pointer to 声明符:

(*D2)[5]

我们必须插入括号,因为下标运算符[N] 比解引用运算符* 绑定得更好,否则它将被读取为*(D2[5]),这不是我们想要的。现在我们有pointer to array of 5

现在让我们执行array of 3。将 D2 替换为 D3[3] 会产生以下结果:

(*D3[3])[5]

太好了,现在我们有了array of 3 pointer to array of 5。只需放置该声明符所属的基类型即可产生完整的声明:

char (*D3[3])[5];

这当然是完全不同的事情 :) 您可以使用它来存储指向您的另一个数组的指针,该数组的类型为 array of 3 arrays of 5 char。让我们将指向a 的第一个子数组的指针存储到D3[0] 中。我们之前发现a[0] 的类型为char[5]。我们可以将指向该数组的指针存储到D3[0] 中,因为D3 是指向char[5] 的指针数组,真是巧合!

D3[0] = &a[0]; // works!

我希望这个小练习已经向您展示了指针和数组之间的一些关系。干杯!

【讨论】:

    【解决方案2】:

    我发现您的代码存在一些问题。首先(从上面复制):

    int x, y;
    for( x=0; x<3; x++ ){
        for( x=0; x<3; x++ ){
            printf( a[x][y] );
        }
    }
    

    在您的内部循环中,您似乎想要使用 y 而不是 x,并且您希望 y 从 0..5 开始。目前,您正在重复变量 x。此外,您的 printf() 语句有问题。这是一些更正的代码:

    int x, y;
    for( x=0; x<3; x++ ){
        for( y=0; y<5; y++ ){
            printf("%d\n", a[x][y] );
        }
    }
    

    其次,当您初始化数组时,您的代码几乎是正确的。这是修正后的版本:

    char a[3][5] = {
                   {1,0,0,0,1},
                   {1,0,0,0,1},
                   {1,0,0,0,1}
                   };
    

    (我在最后“行”数据之后删除了, - 这是一个语法错误。)

    您发布的第二个语法不正确(没有大括号的那个)。

    【讨论】:

      【解决方案3】:

      您得到了错误的结果,因为您使用了两次x(看起来像是复制/粘贴错误)。试试

      int x, y;
      for (x = 0; x < 3; x++) {
          for (y = 0; y < 5; y++) {
              printf("%c", a[x][y]); // Emil H says "%d" might be more useful
          }
      }
      

      编辑:我不确定该教程有什么令人困惑的地方。它完全等同于您的代码,除了不是打印数组,而是将每个元素设置为 (row*column) (其中行和列都是从一开始的,因此是 +1)。

      【讨论】:

      • 我认为他会想要使用 %d,因为输出空字节不是很有用。我一开始输入了错误的顺序。对于那个很抱歉。 :)
      • 令人困惑的事实是,通常宽度 == x,高度 == y 并且他像 [height][width](或 [y][x])一样声明它并阅读它不是真的是我所说的 rowscols 结构,但 colsrows
      • 谢谢,开心果。我只是看到有 HEIGHT 和 WIDTH 并没有注意到哪个维度。
      【解决方案4】:

      教程中的图片很好地展示了数据: alt text http://www.cplusplus.com/doc/tutorial/arrays/bidimensional_arrays3.gif

      从示例中,嵌套的for 循环逐行遍历(左 -> 右)数组并为每一列填充一个值。

      【讨论】:

      • 但我不知道图像和这个之间的关系:int jimmy [HEIGHT][WIDTH];在我看来,像 5x3 结构一样声明和访问 3x5 结构。你能解释一下吗?
      • 这是您希望如何访问数组的问题。如果您将图表横向翻转,您将得到相同的阵列,但现在它更像是 3x5。如果这对您更有意义,您可以这样绘制数组表示图,它仍然是同一个数组。
      【解决方案5】:

      当你可能想要“%d”时使用“%c”这样的小错误并不是你真正想要得到的答案,我谦虚地认为。您说您对数组有些困惑,并且还提到您希望将指针视为包含数组的数组的数组元素。你

      数组数组定义中的最后一个逗号不是语法错误。从 C99 开始允许。

      真正能解决问题的是知道数组不是指针。确实,数组名可以用作常量指针(指向第一个元素),并且指针可以像数组一样被索引。然而,这并不意味着数组和指针是相同的。他们不是。

      您询问程序中的符号“a”实际上是什么。它是一个数组数组。内存布局可以被可视化为一条长线,分成三部分,但仍然是一条连续的线。然后以相同的方式将这些中的每一个切成五个部分。寻址一个元素时,必须使用两个索引;首先是你想要的五元素数组,然后是你想要的第五个元素。

      内存布局不像行和列的网格。内存由一个标量寻址,因此它是线性的。指针算术可能是你可以研究的下一件事情,看看指针上的增量是如何工作的。

      【讨论】:

        【解决方案6】:
        int x, y;
        for( x=0; x<3; x++ ){
            for( x=0; x<3; x++ ){
                printf( a[x][y] );
            }
        }
        

        您需要将第二个 for 循环更改为引用“y”而不是“x”。

        希望有帮助。

        【讨论】:

          【解决方案7】:

          您以错误的方式使用 printf。试试:

          printf("%d", a[x][y]);
          

          我不确定您是要使用 %c 还是 %d。如果要打印数字,请使用 %d。如果要打印实际字符,请使用 %c。

          【讨论】:

            【解决方案8】:

            字符 a[3][5];

            那么,这是一个由 3 个指针组成的数组吗? 5个字符数组还是什么?

            是的,就是这样。尽管您也可以将其作为一组连续的 15 个字符来操作。

            按照惯例,大多数人会认为它表示一个 3 行 5 列的矩阵(我认为),但数据结构本身并没有什么要求。您可以轻松地使用它来表示 5 行和 3 列。或者 3 个集合,每个集合包含 5 个元素,但彼此之间根本没有任何有意义的关系。

            用于进行矩阵操作的特定库对此有一个约定。

            【讨论】:

            • 没有指针。它是 15 个字节的数据,分成 3 组,每组 5 个。
            • 我可以将其转换为指针并使用该指针访问数据,因此从概念上讲,我认为将其称为指针是合理的。
            • 事实上,我可以将数组变量用作指向指针的指针而无需强制转换: int main( int argc, char** argv ) { char a[3][5] = { { 0,1,2,3,4},{5,6,7,8,9},{10,11,12,13,14},}; printf("%d\n", **a);所以,我称之为指针。当然,它是一个常量指针。
            • Dave,您可以这样做,因为存在从数组到其第一个元素的隐式转换。你可以用一个函数做同样的事情: ************************************main;已验证。但是你永远不会说 main 是一个指针-指针-指针-....-指向函数的指针。
            猜你喜欢
            • 1970-01-01
            • 2017-07-07
            • 2021-10-02
            • 2013-01-26
            • 2016-05-08
            • 1970-01-01
            • 2021-08-23
            • 1970-01-01
            相关资源
            最近更新 更多