代码之家  ›  专栏  ›  技术社区  ›  Vyacheslav Verkhovin

C中二维数组的Typedef和指针上的访问

  •  1
  • Vyacheslav Verkhovin  · 技术社区  · 1 年前

    我为表(二维数组)创建自定义类型:

    typedef int table_t[4][2]; // 4 rows, 2 columns
    

    接下来,我声明table和指向该表的指针:

    table_t a = { {10, 0},    // my table
                  {20, 0}, 
                  {30, 0}, 
                  {40, 0} };
    
    table_t *b = &a;          // pointer to my table
    

    接下来,我尝试打印表格的第一列:

        printf("%d\n", a[0][0]);    // var1. 
        printf("%d\n", a[1][0]);    // It's work    
        printf("%d\n", a[2][0]);
        printf("%d\n", a[3][0]);
    

    它有效。如何通过指向table_t的指针访问表的元素?

    我尝试在指针上打印表的第一列:

        printf("%d\n", *b[0][0]);   // var2. 
        printf("%d\n", *b[1][0]);   // It's print true value only from *b[0][0] and
        printf("%d\n", *b[2][0]);   // some strange from other elements
        printf("%d\n", *b[3][0]);
    

    也许我做错了什么?突然间,对我来说下一个代码有效了,但为什么表已经旋转了?:

        printf("%d\n", *b[0][0]);   // var3
        printf("%d\n", *b[0][1]);   // Suddenly, it's work too.
        printf("%d\n", *b[0][2]);   
        printf("%d\n", *b[0][3]);
    

    Godbolt演示: https://godbolt.org/z/7bb63vGxT

    1 回复  |  直到 1 年前
        1
  •  0
  •   chqrlie    1 年前

    b 具有类型 指向的指针 table_t ,因此您应该通过访问该表 *b ,并且由于后缀运算符的优先级高于前缀运算符,因此必须使用括号 b 在取消引用表元素之前:

    void print_column_zero(table_t *b) {
        printf("%d\n", (*b)[0][0]);
        printf("%d\n", (*b)[1][0]);
        printf("%d\n", (*b)[2][0]);
        printf("%d\n", (*b)[3][0]);
    }
    
    [...]
    
        // passing a pointer to the table
        print_column_zero(&a);
    

    请注意,不要使用指向的指针 表_t ,您可以写道:

    void print_column_zero(table_t b) {
        printf("%d\n", b[0][0]);
        printf("%d\n", b[1][0]);
        printf("%d\n", b[2][0]);
        printf("%d\n", b[3][0]);
    }
    
    [...]
    
        // passing a table, which decays to a pointer to its first element
        print_column_zero(a);
    

    自从 表_t 是数组的typedef,这相当于: void print_column_zero(int (*b)[2])... print_column_zero(&a[0]) ,即:将传递指向表元素的指针,就像 void print_column_zero(table_t *b) 但是具有不需要解引用的类型。生成的代码即使不完全相同,也应该非常相似。

    指向数组的指针有些混乱,在实践中很少使用。您可能希望将表封装在一个结构中:

    typedef struct table_t {
        int table[4][2]; // 4 rows, 2 columns
    } table_t;
    
    table_t a = {{ { 10, 0 },    // my table
                   { 20, 0 }, 
                   { 30, 0 }, 
                   { 40, 0 } }};
    
    void print_column_zero(table_t *b) {
        printf("%d\n", b->table[0][0]);
        printf("%d\n", b->table[1][0]);
        printf("%d\n", b->table[2][0]);
        printf("%d\n", b->table[3][0]);
    }
    
    [...]
    
        printf("%d\n", a.table[0][0]);
        printf("%d\n", a.table[1][0]);
        printf("%d\n", a.table[2][0]);
        printf("%d\n", a.table[3][0]);
    
        print_column_zero(&a);    // same output
    
        table_t *b = &a;          // pointer to my table
        print_column_zero(b);     // same output again
    

    与以前的方法相比,这种方法没有增加任何开销,但 struct 封装允许添加可能变得有用的相关数据,并允许使用单个赋值操作复制表。

        2
  •  0
  •   Lundin    1 年前

    将数组(或指针)隐藏在typedef后面不是一个好主意,因为这会使程序更难读取和维护。正如你所注意到的,bug更容易编写。

    问题是 [] 具有比更高的运算符优先级 * 取消引用。

    • 的实际类型 b int (*)[4][2] .
    • 所以如果 *b[i][j] ,第一个 [i] 取消引用指针
    • 第二个 [j] 给我们数组编号 j 带有类型 int [2] .
    • 这个 * 给出该数组中的第一个项,因为“数组衰减”意味着我们在这里得到一个指向第一个项的指针。

    例如 *b[0][1] 将给出类型的第二个数组 int[2] * 第一项, 20 。并不像预期的那样。

    显而易见的解决方案是写 (*b)[0][0] .

    更好的解决方案是去掉typedef并将指针声明为 int (*b)[2] = a; 。这消除了额外的间接性,使您可以键入 b[i][j] .