[英]C array pointer maths. Have memory violation on access by index
我有30年没有使用C了,所以对一个菜鸟问题深表歉意。 但我不能弄错。
我为3x3矩阵分配空间。
然后我想使用数组索引访问它并获取内存冲突。
double **m = (double **)malloc(3 * 3 * sizeof(double));
m[0][0] = 2; <-- exception
我的指针算法有什么问题吗?还是VC ++编译器有新问题? 如果使用其他编译器,其行为会有所不同吗?
编辑:我在inet上读了很多“只读内存”的注释。 我可以关闭它吗?
您只为m
(具有错误的大小)分配内存,而不为m[0]
, m[1]
和m[2]
分配内存。
double **m = malloc(3 * sizeof(double *));
for (int i = 0; i < 3; i++)
{
m[i] = malloc(3 * sizeof(double));
}
切记在不使用它们时释放它们。
您没有分配足够的空间,您需要为3个double
指针和9个doubles
指针分配空间,而您仅分配了9个double
。
以下应该工作
double **m = malloc(3 * sizeof(double *));
m[0] = malloc(3 * sizeof(double));
m[1] = malloc(3 * sizeof(double));
m[2] = malloc(3 * sizeof(double));
m[0][0] = 2.0;
您不需要malloc
转换malloc
的返回值,因为在c中, void *
将自动转换为任何类型的指针。 同样,如果矩阵为n
× n
,则可以使用for (i = 0 ; i < n ; ++1)
循环。
重要提示
如果打算将其编译为C代码,请确保您的编译器实际上是将其编译为C而不是C ++,否则我在下面编写的代码将无法工作。
如果打算将其编译为C ++,请使用new
而不是malloc
(或者最好使用vectors)
如果打算将其编译为C或C ++,则创建一个抽象层,并将malloc
用于C实现,并将new
用于C ++实现。
现在我们已经解决了这个问题...
让我们从另一端开始。 假设您将3x3矩阵声明为
double m[3][3];
请记住,除非它是sizeof
或一元&
运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,否则将转换类型为“ T
N元素数组”的表达式 (“ decay”)到类型为“指向T
指针”的表达式,该表达式的值将是数组第一个元素的地址。
因此,表达式m[i][j]
为double
(duh)。 表达式m[i]
的类型为“ double
3元素数组”或double [3]
; 除非它是sizeof
或一元&
运算符的操作数,否则它将衰减为“ pointer to double
”或double *
类型的表达式。
现在需要注意的是:表达式m
的类型是“ 3-元素数组double
的3-元素数组”或double [3][3]
; 除非它是sizeof
或一元&
运算符的操作数,否则它将衰减为“指向double
3元素数组的指针”或double (*)[3]
而不是 double **
的类型的表达式。 因此,如果要动态分配3x3矩阵,则希望m
的类型为double (*)[3]
,而不是double **
:
double (*m)[3] = malloc( 3 * sizeof *m );
表达式*m
的类型是“ double
3元素数组”,因此sizeof *m == sizeof (double [3]) == 3 * sizeof (double)
。
当您将内存分配为
double **m = malloc( 3 * 3 * sizeof (double));
您将一维的指针数组分配给double
,但是您预留了错误的内存量,并且数组的每个元素都包含不确定的指针值,这就是为什么写入m[0][0]
引发异常。 您必须分两个阶段分配这样的数组:
double **m = malloc( 3 * sizeof *m ); // allocates space for 3 pointers to double
for ( int i = 0; i < 3; i++ )
m[i] = malloc( 3 * sizeof *m[i]); // allocates space for 3 doubles
如果您需要连续的内存,请使用第一种方法。 如果内存不必是连续的,则可以使用第二种方法。
将其推广到NxM矩阵:
如果您知道编译时的行数和列数,并且内存必须是连续的,请使用以下命令:
T (*m)[COLS] = malloc( ROWS * sizeof *m );
如果你不知道在编译时的行和列的数量,您使用的是C99编译器或C编译器2011,支持变长数组,使用以下命令:
size_t rows, cols;
rows = get_rows();
cols = get_cols();
T (*m)[cols] = malloc( rows * sizeof *m );
如果您不知道编译时的行数和列数,并且可变长度数组不可用, 或者矩阵太大,以至于您无法将其分配为单个连续块,请使用以下命令:
T **m = malloc( rows * sizeof *m );
if ( m )
{
for ( size_t i = 0; i < rows; i++ )
{
m[i] = malloc( cols * sizeof *m[i] );
}
}
malloc
返回void*
。 但是您将结果用作指针数组。
如果要连续分配整个矩阵,则应使用以下代码访问每个元素:
m[ROW*columns + COLUMN] = 2;
其中m
定义为:
double *m = malloc(rows * columns * sizeof(double));
类型double **
不对应于指向二维数组第一个元素的指针。
正确的代码如下所示
double( *m )[3] = double ( * )[3] malloc( 3 * 3 * sizeof( double ) );
m[0][0] = 2;
为了释放数组,您可以编写
free( m );
至于double **
类型,则它可能是指向类型为double * [3]
的一维数组的第一个元素的指针。
例如
double **m = ( double ** )malloc( 3 * sizeof( double * ) );
在这种情况下,要使用指向已分配内存的指针来初始化数组的每个元素,应使用循环。 例如
for ( size_t i = 0; i < 3; i++ ) m[i] = malloc( 3 * sizeof( double ) );
因为更清楚地考虑以下声明
double a1[3][3];
double ( *pa1 )[3] = a1;
double * a2[3];
double **pa2 = a2;
如果只是3x3矩阵,则可以将其保留在堆栈中:
double m[3][3];
m[0][0]=2.0;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.