【问题标题】:Stepping through a multidimensional array using pointers in C使用 C 中的指针遍历多维数组
【发布时间】:2016-01-12 00:50:38
【问题描述】:

作为练习,我正在尝试构建一个随机整数的二维数组。我想通过使用指针算法遍历数组来分配随机数。

我认为我在使用以下 For 循环时遇到了问题,我从 King 的 C 编程的 p268 中提取了该循环。

int *p;
for (p = &a[0][0]; p <= &a[NUM_ROWS-1] [NUM_COLUMNS - 1]; p++)

我正在尝试在自己的程序中使用类似的循环,但该程序似乎没有分配任何值。

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

int ** make_array(int in_size );
void read_array( int ** a, int n);

int main(void){
        int size;
        int **p;

        size = 5;
        p = make_array( size );
        read_array( p, size );

        return 0;
}

int ** make_array( int in_size ) {
        int i,  *p, **a;

        srand ((unsigned)time(NULL));

        a = malloc(in_size * sizeof(int*));
        for (i = 0; i < in_size ; i++) {
              a[i] = malloc( in_size * sizeof(int));
        }

        for (p = &a[0][0]; p <= &a[in_size -1 ][in_size - 1]; p++) {
                *p = rand() % 10;
        }

        return a;

}

void read_array( int **a, int n ) {
        int i, *p;
        for (p = &a[0][0] ; p <= &a[n - 1][n - 1]; p++)
                printf("%d ", *p );
}

现在我知道我可以很容易地使用嵌套的 for 循环遍历它,这似乎是一种遍历数组的优雅方式。知道我做错了什么吗?

【问题讨论】:

  • 您的主要问题是这不是二维数组,而只是它的模拟。这种技术属于博物馆。您只需要使用int (*a)[in_size] = malloc(sizeof(int[in_size][in_size])); 分配一个二维数组,然后您就可以一次性获得一个连续数组。对于这样的数组,即使是粗略的初始化技术(实际上什么也没有)也可以。

标签: c arrays pointers multidimensional-array


【解决方案1】:

如您所知,在 C 中,没有二维数组。只有模拟二维数组索引的方法。它们分为两类,(1)创建一个指向数组的指针数组,以及(2)创建一个正常的顺序数组并使用索引算法以二维方式引用元素。在每种情况下,可以根据数组中的元素总数(或数组的size)和要模拟的数组的列数(或stride)来考虑算术。知道数组的sizestride,通过仔细的索引,您可以将C - 1D 数组用作2D 数组。为了帮助解决与这两种类型以及指针和索引的使用有关的任何问题,请考虑以下事项:

要输入的指针数组

首先,当您使用 指向类型的指针数组(例如 int **array)时,您分配 ROWS 指向 COLS 大小的类型数组(本质上是 @987654328 @ number of COLS 大小的数组。)然后通过取消引用,您可以将元素索引为 2D 数组(例如 array[0][x] 其中 0 &lt;= x &lt; COLS 读取第一个指针 array[1][x] 指向的数组中的所有内容,第二个指针,等等……)。

要将指针数组分配给类型,请分配ROWS 的指针数量(其中ROWS 等效于size/stride):

    int **array = NULL;
    ...
    array = xcalloc (size/stride, sizeof *array);

注意: xcalloc 只是一个使用 calloc 并带有错误检查来验证分配的函数)

分配ROWS 的指针数量后,您为每个原始指针分配一个单独的包含COLS(或stride)元素的列数组。

    for (i = 0; i < size/stride; i++)
        array[i] = xcalloc (stride, sizeof **array);

分配指针数组后,您将使用两个循环来填充/操作数组中的数据:

    for (i = 0; i < size/stride; i++)
        for (j = 0; j < stride; j++)
            array[i][j] = rand() % 1000;

您可以使用简单的array[i][j] 语法访问任何单个成员。请记住,您只需将size/stride 视为您的ROWS,将stride 视为您的COLS,因此如果您正在计算ROWSCOLS 的值,您可以将以上内容写为:

    for (i = 0; i < ROWS; i++)
        for (j = 0; j < COLS; j++)
            array[i][j] = rand() % 1000;

将线性阵列视为二维阵列

由于在这种情况下您使用传统的顺序一维数组来保存数据,因此声明和分配数组很简单:

    int *array = NULL;
    ...
    array = xcalloc (size, sizeof *array);

注意:要以模拟 2D 方式访问数组的元素,您必须使用与访问值相同的逻辑将值存储在数组中,从循环的角度来看将与您在上面的指向数组的情况下所做的完全相同。唯一的区别是索引的计算:

    for (i = 0; i < size/stride; i++)
        for (j = 0; j < stride; j++)
            array[i * stride + j] = rand() % 1000;

在这里,需要仔细看看您是如何模拟 array[i][j] 访问的。注意:数组的索引:

    array[i * stride + j] = rand() % 1000;

当您有一个线性一维数组元素时,允许您以二维方式处理和访问值的索引由array[i * stride + j] 给出,其中ij 代表ROWSCOLS

将所有这些放在几个示例中,将向您展示所有部分如何组合在一起:

示例 - 要键入的指针数组

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

void *xcalloc (size_t n, size_t s);

int main (int argc, char **argv) {

    int **array = NULL;
    int size   = argc > 1 ? (int)strtol(argv[1], NULL, 10) : 36;
    int stride = argc > 2 ? (int)strtol(argv[2], NULL, 10) : 6;
    int i,j;

    /* test valid size/stride */
    if (size < stride || size % stride) {
        fprintf (stderr, "error: invalid stride '%d' for %d element array.\n",
                stride, size);
        return 1;
    }

    srand (time(NULL)); /* initialize seed */

    /* alloc array of pointers to array of integers in memory */
    array = xcalloc (size/stride, sizeof *array);

    /* allocate arrays of integers */
    for (i = 0; i < size/stride; i++)
        array[i] = xcalloc (stride, sizeof **array);

    /* fill with random values */
    for (i = 0; i < size/stride; i++)
        for (j = 0; j < stride; j++)
            array[i][j] = rand() % 1000;

    /* printing in simulated 2D format */
    printf ("\n printing (%d x %d) array\n\n",
            size/stride, stride);

    for (i = 0; i < size/stride; i++) {
        for (j = 0; j < stride; j++)
            printf (" %4d", array[i][j]);
        putchar ('\n');
    }

    /* print a particular element array[1][2] */
    if (stride > 1)
        printf ("\n array[1][1] in (%d x %d) array : %d\n\n",
                size/stride, stride, array[1][1]);

    /* free allocated memory */
    for (i = 0; i < size/stride; i++)
        free (array[i]);
    free (array);

    return 0;
}

/** xcalloc allocates memory using calloc and validates the return.
 *  xcalloc allocates memory and reports an error if the value is
 *  null, returning a memory address only if the value is nonzero
 *  freeing the caller of validating within the body of code.
 */
void *xcalloc (size_t n, size_t s)
{
    register void *memptr = calloc (n, s);
    if (memptr == 0)
    {
        fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    return memptr;
}

使用/输出示例

$ ./bin/array_stride_2d 12 2

 printing (6 x 2) array

  535   68
   45  815
  348  480
  417  151
  443  789
  267  738

 array[1][1] in (6 x 2) array : 815

$ ./bin/array_stride_2d 12 3

 printing (4 x 3) array

  841  195  147
  870   18  892
  624  516  820
  250  769  532

 array[1][1] in (4 x 3) array : 18

$ ./bin/array_stride_2d 12 4

 printing (3 x 4) array

  116  275  740  510
  625  122  386  623
  624  879  970  396

 array[1][1] in (3 x 4) array : 122

$ ./bin/array_stride_2d 12 6

 printing (2 x 6) array

  543  631  562  504  307  940
  932   75  225  662  181  990

 array[1][1] in (2 x 6) array : 75

示例 - 将线性数组视为二维数组

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

void *xcalloc (size_t n, size_t s);

int main (int argc, char **argv) {

    int *array = NULL;
    int size   = argc > 1 ? (int)strtol(argv[1], NULL, 10) : 36;
    int stride = argc > 2 ? (int)strtol(argv[2], NULL, 10) : 6;
    int i,j;

    /* test valid size/stride */
    if (size < stride || size % stride) {
        fprintf (stderr, "error: invalid stride '%d' for %d element array.\n",
                stride, size);
        return 1;
    }

    srand (time(NULL)); /* initialize seed */

    /* alloc array of size sequential in memory */
    array = xcalloc (size, sizeof *array);

    /* fill with random values */
    for (i = 0; i < size/stride; i++)
        for (j = 0; j < stride; j++)
            array[i * stride + j] = rand() % 1000;

    /* printing in simulated 2D format */
    printf ("\n printing (%d x %d) array\n\n",
            size/stride, stride);

    for (i = 0; i < size/stride; i++) {
        for (j = 0; j < stride; j++)
            printf (" %4d", array[i * stride + j]);
        putchar ('\n');
    }

    /* print a particular element array[1][2] */
    if (stride > 1)
        printf ("\n array[1][1] in (%d x %d) array : %d\n\n",
                size/stride, stride, array[1 * stride + 1]);

    free (array);

    return 0;
}

/** xcalloc allocates memory using calloc and validates the return.
 *  xcalloc allocates memory and reports an error if the value is
 *  null, returning a memory address only if the value is nonzero
 *  freeing the caller of validating within the body of code.
 */
void *xcalloc (size_t n, size_t s)
{
    register void *memptr = calloc (n, s);
    if (memptr == 0)
    {
        fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    return memptr;
}

使用/输出

$ ./bin/array_stride_1d 12 2

 printing (6 x 2) array

  220  155
  755   51
  427  270
  691  597
  982  995
    4  444

 array[1][1] in (6 x 2) array : 51

$ ./bin/array_stride_1d 12 3

 printing (4 x 3) array

  990  837  473
  153   10  337
  139  940  444
  768  625  457

 array[1][1] in (4 x 3) array : 10

$ ./bin/array_stride_1d 12 4

 printing (3 x 4) array

  617  943  444  396
   38  357  103  441
  646  416   40  586

 array[1][1] in (3 x 4) array : 357

$ ./bin/array_stride_1d 12 6

 printing (2 x 6) array

  364   61  373  723  994  849
  793  332  913  991  999  373

 array[1][1] in (2 x 6) array : 332

内存错误检查

$ valgrind ./bin/array_stride_1d 12 6
==21560== Memcheck, a memory error detector
==21560== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21560== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21560== Command: ./bin/array_stride_1d 12 6
==21560==

 printing (2 x 6) array

  359  841  728  356  563  487
  626   58  823  270  860  896

 array[1][1] in (2 x 6) array : 58

==21560==
==21560== HEAP SUMMARY:
==21560==     in use at exit: 0 bytes in 0 blocks
==21560==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==21560==
==21560== All heap blocks were freed -- no leaks are possible
==21560==
==21560== For counts of detected and suppressed errors, rerun with: -v
==21560== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

希望这有助于您了解在 C 中基本上有两种将数组视为二维数组的方案。您甚至可以更有创意并创建远远超出 2D 的维度数组,只需注意索引计算很快就会变得更多涉及。即使在 2D 级别,您也可以从这些方法中获得更多信息,例如行向量和列向量、上/下矩阵、矩阵算术等。如果您有任何问题,请告诉我。

【讨论】:

    【解决方案2】:
    int *p;
    for (p = &a[0][0]; p <= &a[NUM_ROWS-1] [NUM_COLUMNS - 1]; p++)
    

    在两种情况下有效:
    (1) 当 'a' 被定义为二维数组时,例如int a[NUM_ROWS][NUM_COLUMNS]。在这种情况下,内存块是连续的,因此使用指针变量遍历二维数组元素是有效的。

    (2) 如下修改 make_array() 函数,以分配连续的内存块以使用上述方法。

    int ** make_array( int in_size ) 
    {
            int i,  *p, **a;
            int *big_chunk;
    
            srand ((unsigned)time(NULL));
    
            a = (int **)malloc(in_size * sizeof(int*));
            big_chunk = (int *)malloc(in_size * in_size * sizeof(int));  <-- change done here
            for (i = 0; i < in_size ; i++) 
            {
                  a[i] = big_chunk + i * insize;                         <-- change done here
            }
    
            /* Other code */
    }
    

    在您原来的 make_array() 函数中, malloc() 不保证在 malloc() 调用的连续迭代中连续的内存块。因此,使用指针遍历二维数组元素是不正确的。例如一旦 'p' 达到 a[0][in_size-1] 那么 p = &a[0][in_size] 将不同于 a[1] -> 第二行的 malloc 地址。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-28
    • 2018-06-30
    • 2012-11-06
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多