【问题标题】:What does prevent a compiler from deducing the size of column? [duplicate]是什么阻止编译器推断列的大小? [复制]
【发布时间】:2021-10-12 01:48:08
【问题描述】:

为什么我们需要列大小

int arr[][] = { {1,2,3},{1,3,5} };//int arr[][3]

在这个特定的示例中,为什么编译器不能从数据中推断出它必须将每个元素仅包含 3 个元素打包到每一行中?为什么编译器这样做有限制?

如果是的话我可以理解

int arr[][] = { 1,2,3,1,3,5 };

那么编译器没有关于每行需要打包多少数据的信息。

我读了一个类似的问题Why do we need to specify the column size when passing a 2D array as a parameter?。但它不包含答案。

编辑:为避免混淆,我说的是上述数据的确切格式。

【问题讨论】:

  • C 和 C++ 有一些共同点,但它们是两种不同的语言,在细节方面具有不同的规则。请选择一个。
  • 最后,有人决定了编译器必须能够做什么,不能做什么。您可能会认为,通常会留下一些东西:实际上,编译器也可以发现这一点 - 并且至少仅在某些条件下...... - 如果其他(链接的)Q/A 不足以说服你,我不确定要添加什么。
  • 我不确定这方面是否有区别。但是一旦有人引用标准,它要么是一个,要么是另一个,除非他们做了额外的检查工作
  • @PaulSanders OP 小心地使用了一个初始化器,其中所有“子初始化器”都具有相同的长度。他表示编译器可以在这种特殊情况下做到这一点...... :-)
  • @PaulSanders:现在可能有很多可能性。但是如果标准可以说它必须是这样的,那么应该没有任何问题。

标签: c pointers


【解决方案1】:

是什么阻止了编译器推断列的大小?

C 标准。

来自标准:

6.7.9 初始化

要初始化的实体的类型应该是一个未知大小的数组或者一个完整的对象类型,不是变长数组类型。

所以你可以初始化“一个未知大小的数组”,但你不能初始化“一个未知大小的数组的未知大小的数组”。

【讨论】:

  • True :),但是为什么标准不能有这个定义呢?
  • @InQusitive 可以,但不能。是什么阻止了编译器使 1/2 等于 0.5 ?再次是 C 标准。就这么简单。您可以找到 C 编译器可以做的 1000 件事,但如果它违反标准,编译器就不会。
【解决方案2】:
  1. 因为您可能希望数组大小大于推断的大小。喜欢

    int arr[5] = {1, 2, 3};  // Last two elements are zero.
    
  2. 如果你像这样声明数组

    int arr[][] = { {1, 2, 3}, {1, 3, 5, 7} };
    

    编译器应该怎么做,报告错误,因为它需要数组int array[][3],或者生成数组int array[][4]?这个决定留给人来决定。

  3. 二维数组在内存中是平坦的。例如int arr[3][3]int arr[9] 具有相同的存储空间。因此,允许通过一个初始化列表来初始化两个向量,可以认为是将初始化列表值直接放置到平面内存中:

    #include <stdio.h>
    #include <memory.h>
    
    int main(void) {
      int arr2d[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      int arr1d[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      printf("size: %d\n", sizeof(arr2d) == sizeof(arr1d));
      printf("memcmp: %d\n", memcmp(arr2d, arr1d, sizeof(arr2d)));
      return 0;
    }
    // size: 1
    // memcmp: 0
    
  4. 扩展上述内容,下面的所有 4 个函数都以不同的方式声明和初始化同一个数组 int arr[3][3]

    #include <stdio.h>
    
    void arr2_list2(void) {
      int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
      printf("int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:\n");
      for (int i = 0; i < 3; ++i)
        printf("%d %d %d\n", arr[i][0], arr[i][1], arr[i][2]);
      return 0;
    }
    
    void arr2_list1(void) {
      int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      printf("int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:\n");
      for (int i = 0; i < 3; ++i)
        printf("%d %d %d\n", arr[i][0], arr[i][1], arr[i][2]);
      return 0;
    }
    
    void arr2open_list2(void) {
      int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
      printf("int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:\n");
      for (int i = 0; i < 3; ++i)
        printf("%d %d %d\n", arr[i][0], arr[i][1], arr[i][2]);
      return 0;
    }
    
    void arr2open_list1(void) {
      int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
      printf("int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:\n");
      for (int i = 0; i < 3; ++i)
        printf("%d %d %d\n", arr[i][0], arr[i][1], arr[i][2]);
      return 0;
    }
    
    int main(void) {
      arr2_list2();
      arr2_list1();
      arr2open_list2();
      arr2open_list1();
      return 0;
    }
    // int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:
    // 0 1 2
    // 3 4 5
    // 6 7 8
    // int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:
    // 0 1 2
    // 3 4 5
    // 6 7 8
    // int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:
    // 0 1 2
    // 3 4 5
    // 6 7 8
    // int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:
    // 0 1 2
    // 3 4 5
    // 6 7 8
    

    图像int arr[][] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; 是允许的,那么为什么不允许int arr[][] = {0, 1, 2, 3, 4, 5, 6, 7, 8};,但是编译器如何决定人类在语句中的含义

    int arr[][] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
    

    int arr[1][9],int arr[3][3]int arr[9][1]?

【讨论】:

    【解决方案3】:

    简单的答案 - 在像这样的声明中

    int arr[][] = { ... };
    

    元素类型为int [],为不完整类型,不能声明元素类型不完整的数组。初始化器的存在与否不会改变这一点。初始化器只能告诉您给定元素类型的元素数量;它不能告诉你那个元素类型是什么

    相比之下,在声明中

    int arr[] = { ... };
    

    元素类型为int,是一个完整的类型。您仍然需要在初始化程序中确定元素的数量,但它并没有告诉您每个元素需要多大。

    【讨论】:

      【解决方案4】:

      这是因为尽管[][] 语法看起来像数组数组,但在内存中它就像一维数组一样放置。如果我们想象一个 3x3 棋盘,它将被布置为 {1A, 2A, 3A, 1B, 2B, 3B, 1C, 2C, 3C}。以二维方式访问它的唯一方法是将其寻址为[row + column * columnsize]

      现在很明显,您必须知道列大小才能开始考虑访问数组的元素。这个 columnsize 参数存储在哪里?该数组的类型 int (*)[3]列大小是类型的一个组成部分,就像“t”是int 的一部分一样。

      您要的是auto 类型。编译器必须自己确定整个类型。这相当于想要auto arr = 1;auto arr = "foo";。 C 编译器不处理这些技巧。

      请注意,columnsize 与数组的长度完全不同。 int arr[] = {1}int arr[] = {1,2,3} 的类型完全相同,编译器不会关心数组的长度(内存中的实际大小),不超出范围完全是你的责任。这就是为什么一个元素可以不指定的原因。不是因为编译器能算出来,而是因为编译器忽略了它。

      int arr[][2] = {1,2,3,4,5,6}int arr[][3] = {1,2,3,4,5,6} 的类型完全不同,尽管它们在内存中的大小相同。对于编译器来说,它们就像int[1]char[4]

      {{},{}} 只是帮助您的语法糖,而不是编译器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-24
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        • 1970-01-01
        • 2019-05-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多