I'm rambling a little with hacker 2 (the passwords') and, in the meantime, gained some insight about pointers, mem-alloc, 'valgrind', etc... It's been a truly great, challenging experience.
But, while I started digging (just a tiny little bit) deeper into these subjects, I started to feel like I need to learn (well.. a lot) how to better structure my programs, control the flow, and the memory allocation and "free()".
So, as a general 'styleguide-like' question: which are some (basic) recommended practices while allocating (calloc/malloc) and freeing memory?
E.g.: I wrote a few functions in hacker2 that deal, most of them, with strings (declared as char *
). Since I (and I admit it =P) was kind of improvising a little as needed
(and there you have your first style-guide:
don't ever improvise!
;),
I decided to calloc()
mem for most of them inside the function that actually uses them.
But then, if I return such char*, neither can I free it inside the function where I allocated it, nor in main()
. Therefore, I get LEAK SUMMARY: definitely lost [...]
in valgrind
.
So, a general rule could be:
Don't return pointers. Pass them in as arguments, "fill" them with data inside your functions, and free them back in
main()
when you're certain you won't need them anymore.
Do I have a point? Or is this thought completely misleading?
From such rule, a second thought follows:
Declare and
calloc()
(and, as stated, eventuallyfree()
) all your outer variables inmain()
. Only declare and calloc inside functions those inner variables that are discarded as soon as you leave the function's scope (because they contain auxiliar data).
This is all related to another "strategy" I used in my program: as the flow would get more complex (in terms of 'CS50' complexity, that is.. ;) I tried to evaluate (inside each function) different errors (mem alloc, I/O, pass not found...) with different return values.
Then, back in main()
, would I use such values to determine how & why is my program ending, and print out appropriate user messages, and end my program by returning different values.
E.g. (in main()
):
retUnhash = unhash(passwd, cyph, salt, METHOD);
if (retUnhash == 1)
{
printf("%s\n", "ERROR: I/O error.");
return 3;
}
else if (retUnhash == 2)
{
printf("%s\n", "=(");
printf("%s\n", "Sorry, we couldn't guess your password.");
return 4;
}
else if (retUnhash == 3)
{
printf("%s\n", "ERROR: unknown method name.");
return 5;
}
or the like...
But, if I follow such strategy, I risk returning main()
and exiting my program before I get to free all my previously allocated mem-chunks, therefore getting all those ugly definitely lost
signs from valgrind
.
I could either:
free()
all the variables in every brach of theelse if
construct (mmm... don't sound too good =P)don't actually
return
on each brach, but set areturn_value
(and optionally areturn_message
), and at last, after freeing everything, return as in:printf("%s\n", return_message); return return_value;
So, another "rule" derives:
ALWAYS return ONCE in your code.
Does any of this make any sense to anyone? Are there good, compact, begginers' oriented styleguides regarding code structuring? (I feel like this might be a dumb question: there has to be such thing ;)
Or is it just something you eventually get a hang of after writing tons of lines of code?
Is there any way we can avoid getting bad coding habits and learn "the right way to do it" from the very beginning?