簡體   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