Method said:
Allocate your memory, call functions to manipulate it,
and deallocate it in the same scope where you allocated it.
This keeps things clear and straightforward, eliminating
sticky issues of 'ownership' (i.e. determining when and
where to de-allocate). Keep a strict definition of
each function's responsibities. I.e. don't split
responsibilities (such as memory management) among separate
functions.
[snip]
Allocate in the 'controlling' function.
De-allocate it in the same function.
(more complex applications sometimes might cause one
to stray from this practice, but imo it's a good
general guideline to follow.)
I understand the problems of ownership,
but I don't agree with the "good general guidline"
of allocating/deallocating within the same scope.
I think it's generally a good idea.
For one thing, it doesn't limit your function to dynamic allocation.
If your function takes a pointer to memory, instead of allocating
it's own, then it can be memory of kind.
Consider
the following two function prototypes in a basic
linked list implementation
(incomplete code):
/* Allocate a new node and add it to the head of the list */
void push(struct node** headref, int data);
/* Delete the list by deallocating all the nodes */
void deletelist(struct node** headref);
I think this is perfectly valid for a linked
list implementation and is
certainly not a complex application.
I have written functions like that, but the only time that
I ever wrote a list program that actually did something
that somebody else wanted done, I found it simpler to construct
my lists with inline code, rather than with a function.
list_type *gettext(FILE *fd)
{
enum line_types {
Part, Make, Model, Year, Engine,
};
enum line_types line_type;
int rc;
list_type *tail, *head, *c_last, *parts, *p_last;
char line[LINE_LEN + 1];
struct car car = {0};
head = NULL;
c_last = NULL;
p_last = NULL;
line_type = Part;
rc = nonblank_line(fd, line);
while (rc == 1) {
switch (line_type++) {
case Part:
if (*line != ' ') {
parts = malloc(sizeof *parts);
if (parts == NULL) {
fputs("Oh boy!\n", stderr);
return NULL;
}
parts -> next = NULL;
parts -> data = malloc(strlen(line) + 1);
if (parts -> data == NULL) {
fputs("malloc problem 5\n\n", stderr);
return NULL;
}
strcpy(parts -> data, line);
if (((struct car *)tail -> data) -> parts != NULL) {
p_last -> next = parts;
} else {
((struct car *)tail -> data) -> parts = parts;
}
p_last = parts;
line_type = Part;
}
break;
case Make:
strcpy(car.make, line);
break;
case Model:
strcpy(car.model, line);
break;
case Year:
strcpy(car.year, line);
break;
case Engine:
strcpy(car.engine, line);
tail = malloc(sizeof *tail);
if (tail == NULL) {
fputs("malloc problem 6\n\n", stderr);
return NULL;
}
tail -> next = NULL;
tail -> data = malloc(sizeof car);
if (tail -> data == NULL) {
fputs("malloc problem 7\n\n", stderr);
return NULL;
}
*(struct car *)tail -> data = car;
if (head != NULL) {
c_last -> next = tail;
} else {
head = tail;
}
c_last = tail;
line_type = Part;
break;
default:
fprintf(stderr,
"gettext(), line_type is %d\n", line_type);
return NULL;
}
rc = nonblank_line(fd, line);
}
return head;
}
The function reads a text file which is in the form of something
like: (it starts at Car number 1)
....
Car number 32
FORD
BRONCO II
1990
2.9L 177cid Fuel Injected V6 Vin:T
DH-434 MOTORCRAFT (DISTRIBUTOR CAP)
DY-605 MOTORCRAFT (SENSOR, OXYGEN)
Car number 33
FORD
COUNTRY SQUIRE
1986
5.0L 302cid Fuel Injected V8 Vin:F
DH-434 MOTORCRAFT (DISTRIBUTOR CAP)
DY-605 MOTORCRAFT (SENSOR, OXYGEN)
jk6-854 MOTORCRAFT (BELT, SERPENTINE)
....
The file is constructed by another function in the same program,
from various text files which each list all of the cars,
to which a particular part goes to. There is no known limit
to the number of cars which may be in the file,
or to the number of parts that go with each car.
The file lists cars, and the parts which the vendor has,
according to which parts go with which cars.
It loads the information into a list of structures which represent
cars, and each stucture has a pointer to a list of parts.
There are two kinds of lists,
which both use a pointer to void for data,
so they have the same node stuct type, and I was able to write
a little library style file, with general list handling functions.
void list_free(list_type *node, void (*free_data)(void *))
{
list_type *next;
while (node != NULL) {
free_data(node -> data);
next = node -> next;
free(node);
node = next;
}
}
list_type *list_sort(list_type *head,
int (*compar)(const list_type *, const list_type *))
{
return node_sort(head, node_count(head), compar);
}
size_t node_count(list_type *head)
{
size_t count;
for (count = 0; head != NULL; head = head -> next) {
++count;
}
return count;
}
list_type *node_sort(list_type *head, size_t count,
int (*compar)(const list_type *, const list_type *))
{
size_t half;
list_type *tail;
if (count > 1) {
half = count / 2;
tail = list_split(head, half);
tail = node_sort(tail, count - half, compar);
head = node_sort(head, half, compar);
head = sorted_merge(head, tail, compar);
}
return head;
}
list_type *list_split(list_type *head, size_t count)
{
list_type *tail;
while (count-- > 1) {
head = head -> next;
}
tail = head -> next;
head -> next = NULL;
return tail;
}
list_type *sorted_merge(list_type *head, list_type *tail,
int (*compar)(const list_type *, const list_type *))
{
list_type *list, *sorted, **node;
node = compar(head, tail) > 0 ? &tail : &head;
sorted = list = *node;
*node = sorted -> next;
while (*node != NULL) {
node = compar(head, tail) > 0 ? &tail : &head;
sorted = sorted -> next = *node;
*node = sorted -> next;
}
sorted -> next = head != NULL ? head : tail;
return list;
}
int list_sorted(list_type *list,
int (*compar)(const list_type *, const list_type *))
{
if (list != NULL) {
while (list -> next != NULL) {
if (compar(list, list -> next) > 0) {
break;
}
list = list -> next;
}
}
return list == NULL || list -> next == NULL;
}
void list_x2x(list_type *node,
int (*compar)(const list_type *, const list_type *),
void (*data_merge)(list_type *))
{
list_type *next;
if (node != NULL) {
next = node -> next;
while (next != NULL) {
if (compar(node, next) == 0) {
data_merge(node);
node -> next = next -> next;
free(next);
} else {
node = next;
}
next = node -> next;
}
}
}