C中使用2D数组的指针算法

时间:2015-11-15 19:48:19

标签: c pointers multidimensional-array pointer-arithmetic

我需要帮助了解如何在C中使用点算法的点算法。我使用这个网站(http://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/)作为参考(使用示例1,单个指针)。

int numRows = 2;
int numColumns = 3;

double * arrayMatrix = malloc(numRows * numColumns * sizeof(double));

int row = 0;
int column = 0;

printf("\nPlease enter the elements of your augmented matrix:\n");
for(row = 0; row < numRows; row++)
{
    for(column = 0; column < numColumns; column++)
    {
        printf("A[%d][%d]:", row + 1, column + 1);
        scanf("%lf", &arrayElement);
        printf("\n");

        *(arrayMatrix + row * numColumns + column) = arrayElement;
        //arrayMatrix[row + numColumns + column] = arrayElement;
    }
}

// TEST PRINT
for(row = 0; row < numRows; row++)
{
    for(column = 0; column < numColumns; column++)
    {
        printf("%5.2lf", *(arrayMatrix + row * numColumns + column));
        //printf("%5.2lf",  arrayMatrix[row + numColumns + column]);
    }
    printf("\n");
}

我需要帮助理解这是否是将数据输入2D数组的正确方法,以及它是否也是从2D数组打印数据的正确方法。我将第1行的示例数据用作{1,2,3}和第2行为{1,2,3};但是当打印出所有6个元素时,我得到的信息都是0。

我也用这个答案作为参考(How to use pointer expressions to access elements of a two-dimensional array in C?)。具体遵循以下这一行:

int x = *((int *)y + 2 * NUMBER_OF_COLUMNS + 2); // Right!

但是我使用的是双指针而不是整数,但我不知道这是否会导致我的问题,或者是否是其他问题。

编辑 - 稍微更新了代码,但它仍然无法正常工作。

编辑2:这是我最近尝试开始工作的代码的最新更新。从阵列输入和打印数据的所有3种方式都会得到相同的结果(对于数组中的所有值都是0&#39;)

int numRows = 2;
int numColumns = 3;

//double * arrayMatrix = malloc(numRows * numColumns * sizeof(double));
double (*arrayMatrix)[numColumns] = malloc(sizeof(double[numRows][numColumns]));

int row = 0;
int column = 0;

printf("\nPlease enter the elements of your augmented matrix:\n");
for(row = 0; row < numRows; row++)
{
    for(column = 0; column < numColumns; column++)
    {
        printf("A[%d][%d]:", row + 1, column + 1);
        scanf("%lf", &arrayElement);
        printf("\n");

        //*(arrayMatrix + row * numColumns + column) = arrayElement;
        //arrayMatrix[row + numColumns + column] = arrayElement;
        arrayMatrix[row][column] = arrayElement;
    }
}

// TEST PRINT
for(row = 0; row < numRows; row++)
{
    for(column = 0; column < numColumns; column++)
    {
        //printf("%5.2lf", *(arrayMatrix + row * numColumns + column));
        //printf("%5.2lf",  arrayMatrix[row + numColumns + column]);
        printf("%5.2lf", arrayMatrix[row][column]);
    }
    printf("\n");
}

3 个答案:

答案 0 :(得分:5)

我想我明白你想做什么。在我们讨论代码之前,让我们回顾一下并稍微了解一下可以在C中模拟2D数组的不同方法。您有两种基本方法。您可以静态或动态声明一个数组array[row][col] = {{r0c0, r0c1, r0c2, ...}, {r1c0, r1c1, r1c2, ...} ... };,它将创建一个单独的内存块,值顺序存储r0c0, r0c1, r0c2, ..., r1c0, r1c1, r1c2, ...。,或者您可以创建row个指针,每个指针指向单个col元素数组。第二种方法(每个指向row元素数组的col指针数)在内存中不需要是顺序的。它可以,但不要求它。

数组索引表示法array[i][j]将负责处理顺序内存块中的偏移量,以提供对任何单个元素的访问。对于访问col指向的单个array[i]个大小数组中的任何元素,情况也是如此。但究竟发生了什么?

让我们看一下简单的4元素数组的符号,比如array[4]。要访问任何元素,您可以请求任何元素array[0]array[3]访问所有4个元素。什么是array[2]真的?你知道array也是一个指针。您知道要访问指针所占地址的值,您需要取消引用指针。要访问array[0],您只需编写*array,但如何使用指针表示法访问第二个元素?

正如我们前面所讨论的,声明为我们声明此示例的数组中的所有元素都按顺序存储在内存中。因此,要访问数组中的任何元素,您只需要从数组的开头偏移。既然您知道数组的起始地址只是array,那么如果您想要第二个元素,则需要从头开始或1访问元素偏移*(array + 1) - 试试吧。实际上,您可以使用0-3*(array + i)开始访问偏移i处的所有元素,其中0-3*array中的数字。

回顾一下,这也解释了为什么只需使用*(array + 0)即可访问数组中的第一个元素。如果您为第一个元素编写了完整语法+ 0 - 并且您知道*array无效,那么您可以使用*(array + 0) = *array访问第一个元素,因为{ {1}}。

好的,2D案例怎么样?如果array[x]*(array + x),那么array[x][y]是什么?分解。您知道可以将array[x]写为*(array + x),因此array[x][y]可以写为*(array + x)[y](如果我们暂时将stuff替换为*(array + x) ,我们可以写stuff[y]。我们知道如何用指针表示法编写它:*(stuff + y),对吗?现在只需用*(array + x)代替stuff就可以获得*(*(array + x) + y)这是用于以模拟的2D数组方式访问顺序内存块中的任何元素的完整指针符号,这就是当您编写array[x][y]时场景背后发生的事情。

现在让我们谈谈指针算术。声明指针时,您声明指向特定type的指针(void除外)。 type告诉编译器如何使用该指针处理算术。例如,如果您声明:

char array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char *p = array;

编译器知道每个char占用1-byte个内存,因此当您编写*(p + 1)时,您要求char1-byte来自array的开头。如果你写p++;然后*p,情况也是如此。但是会发生什么:

int array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int *p = array;

由于知道typeint并且int4-bytes(取决于平台),因此当您撰写*(p + 1)或{{1你将获得数组中的第二个元素,但是从数组的开头是值p++; *p;4-bytes告诉编译器如何处理任何给定值的指针算术(即偏移)。

你可以用一个最小的例子来解决这个问题:

type

<强>输出

#include <stdio.h>

#define ROWS 2
#define COLS 2

int main (void) {

    int a[ROWS][COLS] = {{ 1, 2 }, { 3, 4 }};
    int *p = *a;
    int i, j;

    for (i = 0; i < ROWS; i++) {
        for (j = 0; j < COLS; j++)
            printf (" %2d", a[i][j]);
        putchar ('\n');
    }

    /* using a pointer to access the values */
    for (i = 0; i < ROWS * COLS; p++, i++)
        printf (" %2d", *p);
    putchar ('\n');

    return 0;
}

现在谈谈你实际问的问题。鉴于我们已经讨论过的所有事情,你宣布什么时宣布:

$ ./bin/array_min
  1  2
  3  4
  1  2  3  4

你是在为什么指针声明和分配空间?指向具有double (*arrayMatrix)[NCOLS] = calloc (NROWS, NCOLS * sizeof **arrayMatrix); 元素的doubles数组的指针。你有多少人需要持有完整的阵列?对于每个包含NCOLS个元素的数组,您需要NROWS个指针。请注意,使用NCOLS代替calloc。语法和它们的作用都有一个重要但微妙的区别。 malloc将为您分配内存,但该内存未初始化且可包含各种内容malloc也分配,但随后将内存初始化为零。在处理数值数组时,这对于防止意外访问未初始化元素(导致未定义的行为)非常有用。结果是callocmalloc之间存在边际速度差异,但是您很难在少于百万分配的任何内容中找到可衡量的差异。< / p>

在此背景下,再次考虑您要做的事情。只需很少的调整,如果你做了类似的事情,你就会更有意义:

calloc

<强>输出

#include <stdio.h>
#include <stdlib.h>

#define NROWS 2
#define NCOLS 3

int main (void) {

    size_t row = 0;
    size_t col = 0;

    /* allocate NROWS (array of pointers to NCOLS doubles). using 
    * calloc will allocate and initialize all elements to zero.
    */
    double (*arrayMatrix)[NCOLS] = calloc (NROWS, NCOLS * sizeof **arrayMatrix);

    /* prompt user for input, validate a proper value is entered */
    printf("\nPlease enter the elements of your augmented matrix:\n");
    for(row = 0; row < NROWS; row++)
    {
        for(col = 0; col < NCOLS; col++)
        {
            while (printf(" A[%zu][%zu]: ", row + 1, col + 1) && 
                scanf("%lf", &arrayMatrix[row][col]) != 1)
                printf("\n");
        }
    }

    /* printf the array of pointers */
    printf ("\n The matrix entered was:\n\n");
    for(row = 0; row < NROWS; row++)
    {
        for(col = 0; col < NCOLS; col++)
        {
            printf(" %5.2lf", arrayMatrix[row][col]);
        }
        printf("\n");
    }

    free (arrayMatrix);

    return 0;
}

内存错误/泄漏检查

在你的动态分配内存的任何代码中,你有2个责任关于任何分配的内存块:(1)总是保留一个指向内存块起始地址的指针,所以,(2)它可以在释放时释放它不再需要了。您必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,并确认已释放已分配的所有内存。对于Linux $ ./bin/arraymatrix Please enter the elements of your augmented matrix: A[1][1]: 1 A[1][2]: 2 A[1][3]: 3 A[2][1]: 4 A[2][2]: 5 A[2][3]: 6 The matrix entered was: 1.00 2.00 3.00 4.00 5.00 6.00 是正常的选择。有许多微妙的方法来滥用可能导致实际问题的内存块,没有理由不这样做。每个平台都有类似的记忆检查器。它们都很简单易用。只需通过它运行您的程序。

valgrind

如果您还有其他问题,请与我们联系。我的手指累了。结果比我预期的要长得多......

答案 1 :(得分:0)

这是一个函数,它将分配一个连续的2d双数组:

double* alloc_array_2d(int Width, int Height){
  return (double*)malloc(Width * Height * sizeof(double));
}

这是一个函数,它将根据x和y坐标获得指向数组元素的指针:

double* get_element_2d(double* Array, int Width, int X, int Y){
  int index = (Width * Y) + X;
  return Array[index];
}

使用这种方法可以想象一个5 x 3阵列看起来像这样:

0  1  2  3  4
5  6  7  8  9
10 11 12 13 14

在计算机内存中它实际上是这样的:

0 1 2 3 4   5 6 7 8 9   10 11 12 13 14

基本思想是一行值的开头位于前一行值的末尾旁边,因此通过将数组的宽度乘以垂直坐标,可以将索引偏移到正确的行中。例如,要访问第二行(7)中的第三个对象,我们调用

get_element_2d(myArray, 5, 2, 1); // 1 * 5 + 2 = 7

请注意,每个坐标都从零开始,就像所有指针算术一样,因此要访问第一行中的元素,您将使用get_element_2d(Array, Width, X, 0)

答案 2 :(得分:0)

如果您之前有double arrayElement;,那么您的第一个代码示例看起来是正确的。

但是,如果您将其作为int arrayElement;或其他一些数据类型,则会解释您所看到的内容,因为%lf只能与double一起使用。

现代编译器能够对此问题发出警告,因此您可以调查正在使用的编译器开关,以确保获得最大可能的警告级别。

要避免此错误,您可以直接扫描到数组中:

scanf("%lf", &arrayMatrix[row * numColumns + column]);

NB。您似乎已经获得了访问数组的每一行的替代变体,您可以从指针表示法更改为索引表示法。一般来说,索引表示法被认为更容易阅读,因此更可取。