如何访问具有1个索引c-2d数组

c9x0cxw0  于 2022-12-11  发布在  其他
关注(0)|答案(3)|浏览(123)

我有以下几点:

#include <stdio.h>

int main() {

    int a[2][2] = { 0,1,2,3};
    printf("\n%d %d \n%d %d\n",a[0][0],a[0][1],a[1][0],a[1][1]);
    printf("%d %d %d %d\n",*a[0],*a[1],*a[2],*a[3]);

    return 0;
}

返回:

0 1 
2 3
0 2 0 1491303602

有没有办法访问索引为1的二维数组?因为数组保存在连续的内存中,难道我们不能这样做吗?
同时:

printf("\n%d %d \n%d %d\n",&a[0][0],&a[0][1],&a[1][0],&a[1][1]);
printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);

生成以下内容:

1137924528 1137924532 
1137924536 1137924540
1137924528 1137924536 1137924544 1137924552

那么,为什么前两个元素的内存地址&a[0][0] = a[0]和&a[0][1] = a[1],而后两个元素的内存地址不匹配呢?

58wvjzkj

58wvjzkj1#

在代码段中

#include <stdio.h>

int main() {

    int a[2][2] = { 0,1,2,3};
    printf("\n%d %d \n%d %d\n",a[0][0],a[0][1],a[1][0],a[1][1]);
    printf("%d %d %d %d\n",*a[0],*a[1],*a[2],*a[3]);

    return 0;
}

该线路

printf("%d %d %d %d\n",*a[0],*a[1],*a[2],*a[3]);

相当于:

printf("%d %d %d %d\n",a[0][0],a[1][0],a[2][0],a[3][0]);

这意味着您访问的对象a超出了界限,这将调用undefined behavior
您可能需要执行以下操作:

#include <stdio.h>

int main() {

    int a[2][2] = { {0,1}, {2,3} };
    int *p = &a[0][0];
    printf("%d %d \n%d %d\n",a[0][0], a[0][1], a[1][0],a[1][1]);
    printf("%d %d %d %d\n", p[0],p[1], p[2], p[3] );

    return 0;
}

另一种写法是这样的:

#include <stdio.h>

int main() {

    int a[2][2] = { {0,1}, {2,3} };
    printf( "%d %d \n%d %d\n", a[0][0], a[0][1], a[1][0], a[1][1] );
    printf( "%d %d %d %d\n", a[0][0], a[0][1],a[0][2], a[0][3] );

    return 0;
}

这些程序在我的编译器上有以下输出:

0 1 
2 3
0 1 2 3

虽然这可能在所有编译器上都有效,但值得注意的是,这样做可能会调用未定义的行为,这取决于您对标准的解释有多严格。这是因为您访问的是第一个超出界限的子数组a[0],而不是整个对象a。请参阅以下问题以了解更多信息:
One-dimensional access to a multidimensional array: is it well-defined behaviour?
如果我用-fsanitize=undefined编译,编译器gcc和clang实际上都为第二个程序提供了一个运行时警告。如果我启用所有警告,编译器clang还提供了一个编译时警告。
基于这些原因,如果你想将一维数组作为二维数组使用,最好声明一个一维数组而不是二维数组,并且自己对一维数组执行索引计算。这样,你就可以确定你所做的是ISO C标准允许的。下面是一个例子:

#include <stdio.h>

#define ROWS 2
#define COLUMNS_PER_ROW 2

int calculate_1D_offset( int row, int column )
{
    return row * COLUMNS_PER_ROW + column;
}

int main( void )
{
    int a[ROWS*COLUMNS_PER_ROW] = { 0, 1, 2, 3 };

    //find the value of a[1][0], as if "a" were a 2D array
    printf( "%d\n", a[calculate_1D_offset(1,0)] );
}

此程序具有以下输出:

2

那么,为什么前两个元素的内存地址&a[0][0] = a[0]和&a[0][1] = a[1],而后两个元素的内存地址不匹配呢?
数组a由2个子数组组成,每个子数组由2个int元素组成,因此int元素的总数为4。
在你的问题中,地址如下:
1137924528是元素a[0][0]的地址。
1137924532是元素a[0][1]的地址。
1137924536是元素a[1][0]的地址。
1137924540是元素a[1][1]的地址。
在你的问题中,

printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);

具有以下输出:

1137924528 1137924536 1137924544 1137924552

最后两个地址超出界限,因为子数组a[2]a[3]不存在。只存在a[0]a[1]
如果a[2]确实存在,其地址将是1137924544,这将是二维数组的第5个int元素的地址,即a[0][4](因为C中的索引是从0开始的)。
但是,我怀疑这些地址实际上是否正确,因为您在打印地址时使用了错误的printf说明符。为了打印地址,您应该使用%p而不是%d,因为%d用于int,不是指针。在指针为64位而int为32位的平台上,32个最高有效位可能会从值中剥离,甚至更糟。

oogrdqng

oogrdqng2#

#include <stdio.h>

int main(void) {

    int a[2][2] = { 0,1,2,3};
    printf("\n%d %d %d %d\n",a[0][0],a[0][1],a[1][0],a[1][1]);
    printf("%d %d %d %d\n",*a[0],*(a[0] + 1), *a[1], *(a[1] + 1));
}

https://godbolt.org/z/3avvnssn5
结果:

0 1 2 3
0 1 2 3
f87krz0w

f87krz0w3#

让我们“绘制”您的数组:

+---------+---------+---------+---------+
| a[0][0] | a[0][1] | a[1][0] | a[1][1] |
+---------+---------+---------+---------+

由此不难看出一种模式,它告诉我们可以使用单个数组来表示所有两个“维度”:

+---------+---------+---------+---------+
| a[0]    | a[1]    | a[2]    | a[3]    |
+---------+---------+---------+---------+

现在我们要做的就是给出一个公式,从矩阵x,y对中计算出一个数组的索引。
在第一个“行”中,我们通过简单地使用列索引来获得索引。对于第二个“行”,我们需要添加“列”的数量。
因此,一般计算为current_row * total_columns + current_column
我将其编码如下:

const int total_rows = 2;
const int total_columns = 2;

char a[total_rows * total_columns] = { 1, 2, 3, 4 };

for (unsigned current_row = 0; current_row < total_rows; ++ total_rows)
{
    for (unsigned current_column = 0; current_column < total_columns; ++ total_columns)
    {
        printf("matrix[%d][%d] = %d\n", current_row, current_column,
               a[current_row * total_columns + current_column);
    }
}

相关问题