I am trying to read a 2d array of values from a file and then trying to copy that to a 2d array of Strings in C. When I ran my code using gdb I got an error that said "munmap_chunk(): invalid pointer" for the line where I "free( tmval )". Then when I ran the code using valgrind I get several "Invalid write size of 8" that happen whenever I try to read or write a string in the 2d array.
The problem happens during this portion of the code:
//allocate memory for transition matrix tm = (char ***) malloc( numstates*NUM_CLASSES-1*4*sizeof( char ) ); //initialize variables needed for loop int i = 0; char *tmval = (char *) calloc( 4, sizeof( char ) ); // read and process all the remaining lines from the file while( fgets(buf, MAXBUF, fp) != NULL ) { //add comment detection if( buf[0] !='#' ){ //allocate transition states for each row using calloc tm[i] = (char **) calloc( NUM_CLASSES-1, (4*sizeof( char )) ); //strok first with space to skip row number ptr = strtok( buf, " " ); for( int j = 0;j<NUM_CLASSES-1;j ){ //allocate space for string in array tm[i][j] = (char *) calloc( 4, sizeof( char ) ); tm[i][j] = "-1d"; } //loop through line to get class numbers and corresponding states ptr = strtok( NULL, " " ); while( ptr!=NULL ){ int cls = strtol( ptr, end, 10 ); tmval = *end 1; tm[i][cls] = tmval; ptr = strtok( NULL, " " ); } //iterate i i ; } } //free up tmval and file fclose( fp ); free( tmval ); free( end );
I believe this maybe the root of the problem, but I'm not sure how to fix it:
tm[i] = (char **) calloc( NUM_CLASSES-1, (3*sizeof( char )) );
The input file for the program is formatted like this
0 0/0d 1/0d 2/1s 3/3s 4/2s 5/2s 6/5s 7/4s 8/4s 10/9d 1 0/9d 1/9d 2/1s 3/1s 4/1s 5/1s 10/9d
The first number is the row number and the number before the / column number for the value that comes after the /.
I think that many of the Valgrind errors are happening because my memory allocation sizes don't match, but I'm not sure how I can fix that because to me they look ok. Also what is causing the error when I try to free up the space for tmval.
CodePudding user response:
You have the line char *tmval = (char *) calloc( 4, sizeof( char ) );
, which allocates some memory and assigns its address to tmval
. You later have the line tmval = *end 1;
which reassigns tmval
the address of some memory that is in buf
. Reassigning the value discards your only copy of the address of the memory that the calloc
allocated, and is effectively a memory leak. The subsequent free(tmval)
is invalid, because the memory that tmval
now addresses was not created by calloc
, but is inside buf
.
Assigning to a pointer does not copy memory. You need memcpy
or strcpy
(or something similar) to make a copy of the data.
CodePudding user response:
The allocation size in tm = (char ***)malloc( numstates*NUM_CLASSES-1*4*sizeof(char))
is bogus because
NUM_CLASSES-1
must be parenthesized.- you compute the size of a 3D packed array but use types that are inconsistent with this:
char ***tm
has nothing to do withchar tm[numstates][NUM_CLASSES-1][4];
Parsing the line of input is incorrect too:
tmval = *end 1;
makestmval
point inside the arraybuf
, so the next statementtm[i][cls] = tmval;
makes the entry in the pseudo-2D array point inside thebuf
array, which will be overwritten immediately upon reading the next line.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_CLASSES 20
#define MAXBUF 1024
// load_matrix allocates and returns a 3D matrix loaded with the transition states
char (*load_matrix(FILE *fp, int numstates))[NUM_CLASSES - 1][4] {
char buf[MAXBUF];
//allocate memory for transition matrix
char (*tm)[NUM_CLASSES - 1][4] = calloc(sizeof(*tm), numstates);
//initialize transition states for all rows
for (int i = 0; i < numstates; i ) {
for (int j = 0; j < NUM_CLASSES - 1; j )
strcpy(tm[i][j], "-1d");
}
// read and process all the remaining lines from the file
while (fgets(buf, MAXBUF, fp) != NULL) {
char *ptr, *end;
//add comment detection
if (buf[0] == '#')
continue;
//strok first with space to skip row number
ptr = strtok(buf, " \t\n");
if (!ptr)
continue;
int row = strtol(ptr, &end, 10);
if (ptr == end || row < 0 || row >= numstates) {
// invalid row
continue;
}
//loop through line to get class numbers and corresponding states
while ((ptr = strtok(NULL, " \t\n")) != NULL) {
int cls = strtol(ptr, &end, 10);
if (end > ptr && *end == '/' && strlen(end) < 4 && cls >= 0 && cls < NUM_CLASSES - 1)
strcpy(tm[row][cls], end);
}
}
return tm;
}
The matrix is allocated in a single call to calloc()
and can be freed with free(tm)
. Here is an example:
int main() {
char (*tm)[NUM_CLASSES - 1][4] = load_matrix(stdin, 100);
[...]
free(tm);
return 0;
}