I am having some issues with a program I am writing in C, and I have surpassed my knowledge. In summary, I need to deep copy a link list from one list to another. The lists have malloc’d data in them and I need to preserve all the data without having pointers pointing at the same information.
I have only posted the code that I think is relevant. If I am missing any important contextual information please let me know.
Here is the code:
matrix.h
typedef struct matrix {
char *name;
int R;
int C;
int dim;
void (*concat_matrices)( struct matrix *A, struct matrix *B, struct matrix *ret );
} Matrix;
void concat_matrices( Matrix *A, Matrix *B, Matrix *ret ) {
int L1 = strlen( A->name );
int L2 = strlen( B->name );
int len = L1 + L2;
char *Ap = (char*)malloc(L1*sizeof(char)); strcpy(Ap,A->name);
char *Bp = (char*)malloc(L2*sizeof(char )); strcpy(Bp,B->name);
char *c = (char*)malloc(sizeof(char)*(len + 2));
c[0] = '('; strcat(c, Ap); strcat(c, Bp); c[len+1] = ')';
ret->name = (char*)malloc(sizeof(char)*(len + 2));
strcpy(ret->name, c);
ret->R = A->R; ret->C = B->C;
ret->dim = ret->R*ret->C;
free(Ap); free(Bp); free(c);
}
matrix_list.h
typedef struct node {
Matrix *M;
struct node *next;
struct node *prev;
} Node;
typedef struct matrix_list {
Node *head;
Node *tail;
int size;
void (*append)( struct matrix_list *list, Matrix *M );
void (*print)( struct matrix_list *list );
void (*reverse_print)( struct matrix_list *list );
void (*delete)( struct matrix_list *list, const char *name );
void (*delete_tail)( struct matrix_list *list );
void (*delete_head)( struct matrix_list *list );
void (*release)( struct matrix_list *list );
void (*clone_list)( struct matrix_list *from, struct matrix_list *to );
} MatrixList;
...
void clone_list( MatrixList *from, MatrixList *to ) {
if( from->head == NULL ) {
to = NULL;
} else {
Node *tmp = from->head;
while( tmp != NULL ) {
Matrix *m_copy = (Matrix*)malloc(sizeof(Matrix*));
char *c_copy = (char*)malloc(sizeof(char*)*strlen(tmp->M->name));
strcpy(c_copy,tmp->M->name);
m_copy->name=c_copy;
m_copy->R=tmp->M->R;
m_copy->C=tmp->M->C;
m_copy->concat_matrices = concat_matrices;
to->append( to,m_copy );
tmp = tmp->next;
}
}
}
main.c
chain->print(chain);
MatrixList *chain_copy = (MatrixList*)malloc(sizeof(MatrixList));
set_list_functions( chain_copy );
chain->clone_list(chain, chain_copy);
chain_copy->print(chain_copy);
The problem arises when I try and print out the clone. Because I am malloc’ing in the clone function I understand the data goes out of scope. How can I do this copy so after the function is called clone has its own version of the data?
UPDATE:
First I would like to thank you all for taking your time to answer my question, and for teaching me more about C. I have only been coding for about 3 years. And I have a lot to learn. The updated source with 0 errors from Valgrind can be found at.
http://matthewh.me/Scripts/c++/matrix_chain/ for anyone else trying to figure out things like me. User = guest Password = guest. The clone_list function now looks like this.
void clone_list( MatrixList *from, MatrixList *to ) {
if( from->head == NULL ) {
to = NULL;
} else {
Node *tmp = from->head;
while( tmp != NULL ) {
Matrix *m_copy = (Matrix*)malloc(sizeof(Matrix));
m_copy->name = (char*)malloc(strlen(tmp->M->name) + 1);
sprintf( m_copy->name, "%s", tmp->M->name );
m_copy->R=tmp->M->R;
m_copy->C=tmp->M->C;
m_copy->concat_matrices = concat_matrices;
to->append( to,m_copy );
tmp = tmp->next;
}
}
}
If anyone else sees anything else wrong and would like to add additional pointers please feel free to do so.
You haven’t allowed for the null that terminates strings, so you have classic buffer overflows.
You also do not need to copy the names three times. Your current code is:
This should be:
This eliminates
Ap,Bpandcaltogether, and avoids the buffer overflow problems. I’m not sure I’d slam the two names together like you do, but that’s your choice.Apparently, this isn’t sufficient of a problem…there are other issues too.
The first assignment zeroes the local pointer; it doesn’t do a thing to the
MatrixListpassed in as the target. This should presumably be:This does a wholesale structure copy, but sets the head and tail to null, and the function pointers are all fine – they can be shared. Assuming that the
sizeinfromwas correctly zero, it will be correct intotoo. (Again, this is probably not the code you are exercising.)The next problem is with the memory allocation:
This allocates one pointer’s worth of memory, not one Matrix’s worth. Use either of these:
That is a major source of your trouble (and one which
valgrindwill find easily).When the
+1gets forgotten once, it gets forgotten many times, but this time it doesn’t cause problems unless the name is the empty string because you multiply by 4 or 8 (32-bit or 64-bit) because of thesizeof(char *)instead of the intendedsizeof(char).This should probably be:
I’d probably use a name like
oldinstead oftmp. I am also remiss in not reminding previously that you should religiously check every return from every memory allocation. Or use a set of cover functions for the memory allocation routines which do the check for you (often calledxmalloc()oremalloc(), etc.).The code below that does not seem to copy the
dimmember across, which is a bug if you ever depend on it.I’m not entirely happy that you seem to rely on the
tolist being appropriately initializes before callingclone_list(). In particular, thehead,tailandsizemembers are not zeroed here, and the function pointers are not set. I’d be happier to see something like:Or even:
The
clone_matrix()function looks like:I downloaded a version your code and it now seems to work, more or less. You should be compiling with at least
-Wallas a compiler option; I refuse to compile with anything less and usually use-Wextratoo. I make too many simple-to-spot mistakes not to make full use of the compiler, and while you are learning, you will too. (I’ve only been coding in C for just over a quarter century; the compiler still catches typos and other silly mistakes, but I seldom have big problems once the code is compiling.)When I turned on
-Wall, there was a problem with the (unused)perm()function because it doesn’t return a value even though it says it will, and there was a problem because the correct definition formain()with arguments isint main(int argc, char **argv)and you were missing one of the stars. Other than that, it seems to be working OK now – you can continue with your development.There is a function in POSIX called
strdup()which duplicates a string reliably. It is useful and avoids the off-by-one mistakes.You should review the use of headers. They are for declarations, primarily. If you explicitly use
inline(your code doesn’t yet), it can be appropriate to includeinlinefunctions in a header, but otherwise, function bodies should not be in headers. They should be in source files (.csuffix). Each header should contain the minimum necessary information for the code that uses the functionality provided by the source to use. It should not include extraneous headers, but it should include all necessary headers. Inmatrix.h, you include<stdio.h>which is unnecessary. And if you removed the code, neither<string.h>nor<stdlib.h>would be needed either.