我需要帮助理解如何在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
的所有元素,其中i
是0-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-byte
从array
开始。如果先写p++;
,再写*p
,情况也是如此。但是:
int array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int *p = array;
由于知道type
是int
,int
是4-bytes
(取决于平台),当您写入*(p + 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);
你正在为一个指向什么的指针声明和分配空间?指向具有NCOLS
个元素的doubles
数组的指针。你需要多少个这样的容器来保存整个数组?您将需要NROWS
个指针指向每个包含NCOLS
个元素的数组。注意上面使用的是calloc
而不是malloc
。在语法和功能上有一个重要但微妙的区别。malloc
将为您分配内存,但该内存未初始化,并且可以包含各种内容。calloc
也分配内存,但随后将内存初始化为零。在处理数值数组时,这有助于防止意外访问未初始化的元素(导致未定义的行为)。结果,malloc
和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: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。你似乎对访问数组的每一行都有不同的变体,从指针表示法改为索引表示法。一般来说,索引表示法被认为更容易阅读,因此更可取。