views:

330

answers:

7

OK, I have this piece of code:

typedef struct faux_crit
{
  char dna[DNALEN+1]; //#define'd to 16
  int x, y;
  int age;
  int p;
  int dir;
} crit;

crit *makeguy(int x, int y)
{
  crit *guy;
  guy = (crit *) malloc(sizeof(crit));
  strcpy(guy->dna, makedna());
  guy->x = x;
  guy->y = y;
  guy->age = guy->p = guy->dir = 0;
  return guy;
}

char *makedna()
{
  char *dna;
  int i;
  dna = (char *) malloc(sizeof(char) * DNALEN+1);
  for(i = 0; i < DNALEN; i++)
    dna[i] = randchar();
  return dna;
}

int main()
{
  int i;
  crit *newguy;
  srand((unsigned) time(0));

  newguy = makeguy(0, 0);
  /*[..]
   just printing things here
   */
  free(newguy);

  return 0;
}

I'd just want to know what did I wrong with managing memory, because valgrind reports a memory error. I presume it's the dna var in makedna, but when should I free it? I don't have access to it after leaving the function, and I need to return it, so I can't free it before that.

EDIT: Okay, thank you all.

+3  A: 

You should store the pointer of makedna() before passing it to strcpy so you will be able to free it once you're done.

char* dna = makedna();
strcpy(guy->dna, dna);
free(dna);
GRB
There are still problems with strcpy, unless makedna is fixed to null terminate the string it creates.
quamrana
Indeed, I admittedly forgot to mention that. Take a look at Chris's answer.
GRB
A: 

Line 3 : dna[DNALEN+1] // statically declared array containing [DNALEN+1] elements - memory is already assigned Line 25: no need for the malloc as the array already exists

Maciek
That `Line 25` refers to a local variable named `dna`, not the member of the struct which is what you are referring to.
paracycle
A: 

You guessed right. The problem is related to the makedna function. However, there is nothing wrong with that function. It is more about how you use it.

It is perfectly OK for makedna to return a pointer to allocated memory. However, it is up to the calling function to release that memory after it is done with it.

Thus I recommend you change:

crit *guy;
guy = (crit *) malloc(sizeof(crit));
strcpy(guy->dna, makedna());

to:

crit *guy;
guy = (crit *) malloc(sizeof(crit));
char * dna = makedna();
strcpy(guy->dna, dna);
// Now that we copied the content of the dna string
// we can free it.
free(dna);

EDIT: As Chris Jester-Young points out, there was a small problem with your makedna function as well. You need to null-terminate the value you return from that function so that strcpy can work correctly. Add this in your makedna function before you return the result:

dna[DNALEN] = 0;
paracycle
There are still problems with strcpy, unless makedna is fixed to null terminate the string it creates.
quamrana
Yes, that is right. I had missed that originally. Will edit the post to reflect that.
paracycle
+7  A: 

You should do this:

char *tempdna = makedna();
strcpy(guy->dna, tempdna);
free(tempdna);

But for the strcpy to work, your makedna function needs to zero-terminate the string. At the end, just before the return, have:

dna[DNALEN] = 0;
Chris Jester-Young
+1 for dna[DNALEN] = 0;
Vijay Mathew
+1 for the same reason as Vijay.
paracycle
There are still problems with strcpy, unless makedna is fixed to null terminate the string it creates.
quamrana
Huh? That's exactly what his `dna[DNALEN] = 0;` line is doing
GRB
A: 

fbereton is right - you didn't need a malloc for DNA.

However, had you been storing malloc'd memory inside your structure, the key point is that you can't just free the structure and expect the other memory allocations to be freed. You'd want to free all the allocations inside the structure before freeing the structure itself.

Wade Williams
The poster is not malloc'ing the `dna` variable in the struct, he is malloc'ing a local variable with the same name.Besides, I think he realized that in order for the `free(guy)` to be a simple call, he needed the `dna` member to be a statically allocated array.
paracycle
+3  A: 

You should change makedna() to take a parameter:

void makedna(char* dna)
{
  int i;
  for(i = 0; i < DNALEN; i++)
  {
    dna[i] = randchar();
  }
}

note the extra braces for clarity

and line 14 becomes:

makedna(guy->dna);

This avoids at least one set of messing about with malloc and free.
Edit:
Also this solution avoids any null-termination problems with strcpy.

quamrana
-1 for the "extra braces for style"; otherwise your post was a good one.
Chris Jester-Young
+1 for the "extra braces for style"
anon
@Neil: Yay for coding style holy wars! :-P
Chris Jester-Young
@anyone: Its not coding style, so much as safety in the future when someone else adds another line below the for. Without braces its not always obvious that its not executed multiple times. With braces you have to think about whether you mean the extra line to be executed by 'for', or afterwards.
quamrana
@Chris Downvoting someone simply because you disagree withn their programming style is obviously wrong, particularly when (as you pointed out) the answer was correct.
anon
WRT adding another line after the `for`: I come from the school of thought that says that your indentation should match the intention of the code, and yes, when there is a second statement added, then braces should be added at the same time. One then mentally learns to read "multiple indented lines without braces" as just as much of a WTF as "switch cases not followed by break (or return, continue, throw, etc.)". Adding braces to single-line statements greatly increases their bulk.
Chris Jester-Young
@Neil: The downvote isn't because I disagree with their programming style, but the way that it's being pushed, with the whole "note my holier-than-thou style" thing (which is how I read the "note the extra braces for style" line). Perhaps I'm just misreading things, in which case I'm happy to retract my downvote (if SO will let me).
Chris Jester-Young
+9  A: 

The easiest workaround is to change makeguy() like this:

char* dna = makedna();
strcpy(guy->dna, dna);
free(dna);

But this is not a good solution as you are allocating memory at one location and freeing it in other. It is better to do the malloc and free at the same place. So I recommend to change makedna() to:

void* makedna(char* dna, int dna_len)
{
  int i;
  for(i = 0; i < dna_len; i++)
    dna[i] = randchar();
}

You can call makedna() like this:

char* dna = (char*)malloc(DNALEN+1);
makedna(dna, DNALEN);
dna[DNALEN] = 0;
strcpy(guy->dna, dna);
free(dna);

Now makedna() only does what it is expected to do: make a dna sequence. Memory management should be taken care of by the caller. Moreover, this solution gives the flexibility of using a static char array, if that is required at a different call site.

Vijay Mathew
+1 for making excellent point about removing redundant allocation.
Chris Jester-Young
Note that even the caller does not need to allocate memory since guy->dna is already allocated. Also you still have problems with strcpy and null termination.
quamrana
Yes the caller need not allocate memory. Thats what I meant by "Moreover, this solution gives the flexibility of using a static char array, if that is required at a different call site". I fixed the null termination bug. Thanks for pointing that out.
Vijay Mathew