c中2D数组的指针运算



我需要帮助理解如何在c中使用2D数组的点算术。我使用这个网站(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");
}

我需要帮助理解,如果这是一个正确的方式来输入数据到一个二维数组,如果它也是一个正确的方式来打印数据从一个二维数组。我使用的例子数据为行1为{1,2,3}和行2为{1,2,3};然而,当打印出所有6个元素的信息时,我得到的都是0。

我也使用这个答案作为参考(如何使用指针表达式访问C中的二维数组元素?)。特别是下面这行:

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

但我使用双指针而不是整数,但我不知道这是否导致了我的问题,或者如果它是别的什么。

编辑-更新了一点代码,但它仍然不工作。

编辑2:这是最近更新的代码,我一直试图得到的工作。从数组中输入和打印数据的所有三种方式都会产生相同的结果(数组中所有值都为0)。
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");
}

我想我明白你想做什么。在我们讨论代码之前,让我们回顾一下,了解一下用c语言模拟二维数组的不同方法。您可以静态或动态地声明一个数组array[row][col] = {{r0c0, r0c1, r0c2, ...}, {r1c0, r1c1, r1c2, ...} ... };,它将创建一个内存块,其值按顺序存储r0c0, r0c1, r0c2, ..., r1c0, r1c1, r1c2, ...,或者您可以创建row数量的指针,每个指针指向col元素的单个数组。第二种方法(每个指针指向col元素的数组的row个数)在内存中不必是顺序的。它可以是,但没有要求它必须是。

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

让我们看看一个简单的4元素数组的表示法,比如array[4]。要访问任何元素,您可以请求任何元素array[0]array[3]来访问所有4个元素。array[2]到底是什么?你知道array也是一个指针。你知道要访问指针保存的地址处的值,你需要对指针解引用。要访问array[0],您可以简单地写*array,但是如何使用指针表示法访问第二个元素呢?

如前所述,在本例中声明的数组中的所有元素按顺序存储在内存中。因此,要访问数组中的任何元素,您所需要的只是从数组开始的偏移量。因为您知道数组的起始地址是array,如果您想要第二个元素,您需要从一开始访问元素偏移量1,或者在*(array + 1)——尝试一下。事实上,你可以从*(array + i)开始访问偏移量为0-3的所有元素,其中i0-3中的一个数字。

回顾一下,这也解释了为什么您可以通过简单地使用*array来访问数组中的第一个元素。如果您为第一个元素编写了完整的语法,那么您将拥有*(array + 0)—并且您知道+ 0没有做任何事情,因此您可以使用*array访问第一个元素,因为*(array + 0) = *array

好的,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)时,你要求char的值1-bytearray开始。如果先写p++;,再写*p,情况也是如此。但是:

会怎样呢?
int array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int *p = array;

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

你可以用一个简单的例子来巩固这一点:

#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);

你正在为一个指向什么的指针声明和分配空间?指向具有NCOLS个元素的doubles数组的指针。你需要多少个这样的容器来保存整个数组?您将需要NROWS个指针指向每个包含NCOLS个元素的数组。注意上面使用的是calloc而不是malloc。在语法和功能上有一个重要但微妙的区别。malloc将为您分配内存,但该内存未初始化,并且可以包含各种内容calloc也分配内存,但随后将内存初始化为零。在处理数值数组时,这有助于防止意外访问未初始化的元素(导致未定义的行为)。结果,malloccalloc之间的速度有一个边际差异,但是在小于几个百万分配的情况下,很难找到一个可测量的差异。

有了这样的背景,再考虑一下你要做什么。只要稍加调整,您就可以使它更有意义,如果您这样做:
#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:nn");
for(row = 0; row < NROWS; row++)
{
for(col = 0; col < NCOLS; col++)
{
printf(" %5.2lf", arrayMatrix[row][col]);
}
printf("n");
}
free (arrayMatrix);
return 0;
}

$ ./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

内存错误/泄漏检查

在你写的任何代码中动态分配内存,你有2个责任关于任何分配的内存块:(1)总是保留一个指向内存块的起始地址的指针,所以(2)它可以在不再需要时被释放。你必须使用内存错误检查程序来确保你没有写超出/超出你分配的内存块,并确认你已经释放了所有你分配的内存。对于Linux,valgrind是正常的选择。有很多微妙的方法可以误用一块内存,从而导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都很容易使用。用它运行你的程序。

$ valgrind ./bin/arraymatrix
==17256== Memcheck, a memory error detector
==17256== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==17256== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==17256== Command: ./bin/arraymatrix
==17256==
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
==17256==
==17256== HEAP SUMMARY:
==17256==     in use at exit: 0 bytes in 0 blocks
==17256==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==17256==
==17256== All heap blocks were freed -- no leaks are possible
==17256==
==17256== For counts of detected and suppressed errors, rerun with: -v
==17256== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
如果你还有问题,请告诉我。我的手指累了。这比我预期的要长得多……

下面的函数将分配一个连续的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

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

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

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

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

为了避免这个错误,你可以直接扫描数组:

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

NB。你似乎对访问数组的每一行都有不同的变体,从指针表示法改为索引表示法。一般来说,索引表示法被认为更容易阅读,因此更可取。

相关内容

  • 没有找到相关文章

最新更新