简体   繁体   中英

C array pointer maths. Have memory violation on access by index

I havent used C for 30 yrs.. so I apologise for a noob question. but i cant get whats wrong.

I allocate space for 3x3 matrix.

then I want to access it using array index and get memory violation.

double **m = (double **)malloc(3 * 3 * sizeof(double));
m[0][0] = 2; <-- exception

is it something wrong with my pointers arithmetic or is it something new in VC++ compiler? will it behave differently if I use another compiler?

EDIT : I read a lot of 'read only memory' remarks on inet.. since when we get it? can I switch it off?

You only allocate memory for m (with the wrong size), not m[0] , m[1] and m[2] .

double **m = malloc(3 * sizeof(double *));
for (int i = 0; i < 3; i++)
{
    m[i] = malloc(3 * sizeof(double));
}

Remember to free all of them when they are not used.

You are not allocating enough space, you need to allocate space for 3 pointers of double and 9 doubles , and you was allocating only for 9 double s.

The following should work

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;

you don't need to cast the return value of malloc since in c, void * is automatically converted to any type of pointer. Also if your matrix is n × n you can use a for (i = 0 ; i < n ; ++1) loop.

IMPORTANT NOTE UP FRONT

If this is meant to be compiled as C code, make sure your compiler is actually compiling it as C , not C++, otherwise the code I've written below won't work.

If this is meant to be compiled as C++, use new instead of malloc (or better yet, use vectors)

If this is meant to be compiled as either C or C++, then create an abstraction layer and use malloc for the C implementation and new for the C++ implementation.

Now that we've gotten that out of the way...

Let's start from the other end. Suppose you declare a 3x3 matrix as

double m[3][3];

Remember that unless it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T ", and the value of the expression will be the address of the first element of the array.

So, the type of the expression m[i][j] is double (duh). The type of the expression m[i] is "3-element array of double ", or double [3] ; unless it is the operand of the sizeof or unary & operators, it will decay to an expression of type "pointer to double ", or double * .

Now for the tricky bit: the type of the expression m is "3-element array of 3-element array of double ", or double [3][3] ; unless it is the operand of the sizeof or unary & operators, it will decay to an expression of type "pointer to 3-element array of double ", or double (*)[3] , not double ** . So if you want to allocate a 3x3 matrix dynamically, you want the type of m to be double (*)[3] , not double ** :

double (*m)[3] = malloc( 3 * sizeof *m );

The type of the expression *m is "3-element array of double ", so sizeof *m == sizeof (double [3]) == 3 * sizeof (double) .

When you allocated memory as

double **m = malloc( 3 * 3 * sizeof (double));

you allocated a single-dimensioned array of pointers to double , but you set aside the wrong amount of memory, and each element of the array contained an indeterminate pointer value, which is why writing to m[0][0] threw an exception. You would have to allocate such an array in 2 stages:

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

If you need the memory to be contiguous, use the first method. If the memory doesn't have to be contiguous, you can use the second method.

Generalizing this to NxM matrices:

If you know the number of rows and columns at compile time and the memory must be contiguous, use the following:

T (*m)[COLS] = malloc( ROWS * sizeof *m );

If you don't know the number of rows and columns at compile time and you are using a C99 compiler or a C 2011 compiler that supports variable-length arrays, use the following:

size_t rows, cols;
rows = get_rows();
cols = get_cols();
T (*m)[cols] = malloc( rows * sizeof *m );

If you don't know the number of rows and columns at compile time and variable-length arrays aren't available, or the matrix is so large that you cannot allocate it as a single, contiguous block, use the following:

T **m = malloc( rows * sizeof *m );
if ( m )
{
  for ( size_t i = 0; i < rows; i++ )
  {
    m[i] = malloc( cols * sizeof *m[i] );
  }
}

malloc returns void* . But you are using the result as an array of pointers.

If you are going to allocate the whole matrix continously then you should access each element with the following code:

m[ROW*columns + COLUMN] = 2;

where m is defined as:

double *m = malloc(rows * columns * sizeof(double));

Type double ** does not correspond to the pointer to first element of a two-dimensional array.

The correct code will look like

double( *m )[3] = double ( * )[3] malloc( 3 * 3 * sizeof( double ) );
m[0][0] = 2;

And for to free the array you could write

free( m );

As for type double ** then it might be a pointer to first element of a one-dimensional array that has type double * [3]

For example

double **m = ( double ** )malloc( 3 * sizeof( double * ) );

In this case that to initialize each element of the array with a pointer to an allocated memory you should use a loop. For example

for ( size_t i = 0; i < 3; i++ ) m[i] = malloc( 3 * sizeof( double ) );

For it would be more clear consider the following declarations

double a1[3][3];
double ( *pa1 )[3] = a1;

double * a2[3];
double **pa2 = a2;

If it's just a 3x3 matrix, you can keep it on the stack:

double m[3][3];
m[0][0]=2.0;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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