繁体   English   中英

C数组指针数学。 按索引访问时发生内存冲突

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM