Hm.... you can do the following (save the pointer for later processing, like free()
ing it)
char *p;
printf("%s", p = my_function(arg));
free(p);
or, even more readably
void *p = my_function(arg);
printf("%s", p);
free(p);
A second approach is to design my_function()
to operate on a preallocated (I'll explain it later) buffer.
char *my_function(some_type arg, char *buffer, size_t buff_sz)
{
/* use buffer as a work buffer */
/* ... */
return buffer;
}
and then use it with some buffer, allocated in an auto
variable:
if (something) {
char buffer[1000];
printf("%s", my_function(arg, buffer, sizeof buffer));
}
/* buffer is gone here */
This is a very flexible way of working, once you have mastered it.
Once I wrote a routine that saved a series of allocated pointers to free()
(or possibly postprocess the pointers) them all once work had finished. In order to allow expressions like the one you proposed, the function took a pointer (the allocated pointer) and saved it in a structure, the function returned its argument, so the construction above was possible
It is composed of only a header file and a simple file that contains two functions:
mf_save()
, to save a pointer for later freeing.
mf_free()
, to post-process (and free) all the pointers saved in one shot. This routine also free()
s the allocated memory for saving the pointers, so the postprocessing is done only once, after that all saved pointers are lost, so you have better to free()
them in the post-processing routine pointer you pass to it. A common use it to pass it just free
.
mf.h
/* mf.h -- types and definitions for mf.c.
* Author: Luis Colorado <luiscoloradourcola@gmail.com>
* Date: Sun Sep 19 12:16:18 EEST 2021
* Copyright: (c) 1992-2021 Luis Colorado. All rights reserved.
* License: BSD.
*/
#ifndef _MF_H
#define _MF_H
#define MF_F(_fmt) "%s:%d:%s: "_fmt, __FILE__, __LINE__, __func__
typedef struct mf_s *mf_t;
void *mf_save(mf_t *safe_ref, void *ptr);
void mf_free(mf_t *safe_ref, void (*proc)(void *));
#endif /* _MF_H */
mf.c
This is the core of the module.
/* mf.c -- a module to save pointers for batch free()ing, after
* work is done.
* Author: Luis Colorado <luiscoloradourcola@gmail.com>
* Date: Sun Sep 19 11:54:37 EEST 2021
* Copyright: (c) 1992-2021 Luis Colorado. All rights reserved.
* License: BSD.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "mf.h"
#define MF_SIZE (3)
#define THIS (*safe_ref)
struct mf_s {
size_t mf_n; /* num of saved in this block */
mf_t mf_nxt; /* nxt chunk pointer */
mf_t mf_vec[MF_SIZE];/* saved pointers */
};
static mf_t
mf_alloc(mf_t prev)
{
/* call calloc, so the block is zeroed */
mf_t ret_val = calloc(1, sizeof * ret_val);
assert(ret_val != NULL);
ret_val->mf_nxt = prev;
return ret_val;
}
void *
mf_save(mf_t *safe_ref, void *ptr)
{
if (THIS == NULL || THIS->mf_n == MF_SIZE) { /* full chunk */
printf(MF_F("full chunk, allocating new [%p]"), THIS);
THIS = mf_alloc(THIS);
printf(" -> [%p]\n", THIS);
}
mf_t s = THIS;
s->mf_vec[s->mf_n++] = ptr;
printf(MF_F("SAVED [%p]\n"), ptr);
return ptr;
}
void
mf_free(mf_t *safe_ref, void (*proc)(void *))
{
while(THIS) {
int ix;
for (ix = 0; ix < THIS->mf_n; ++ix) {
void *p = THIS->mf_vec[ix];
printf(MF_F("processing [%p]\n"), p);
proc(p);
}
mf_t temp = THIS;
THIS = THIS->mf_nxt;
printf(MF_F("freeing chunk @ [%p] -> [%p]\n"), temp, THIS);
free(temp);
}
}
The good news with this approach is that it uses dynamic memory only, and the safe only occupies the size of a pointer in the stack. Another good think is that it was written in 1992, and so it doesn't require any compiler novelties.
test_mf.c
This is a test routine that allocates 10 pointers (randomly sized, but with space to store in them the allocated size ---the rest is not used) and then deallocates them by calling mf_free()
.
#include <stdio.h>
#include <stdlib.h>
#include "mf.h"
/* this is a wrapper to malloc() to store the number of bytes in
* the allocated memory, so the processing() routine has the
* possibility of printing the amount of allocated memory */
static void *
malloc_wrapper(size_t n)
{
int *ret_val = malloc(n);
printf(MF_F("allocated %zd bytes @ %p\n"), n, ret_val);
*ret_val = n;
return ret_val;
}
/* this is a wrapper to free() to printf the number of allocated
* bytes that are being freed. */
void
free_wrapper(void *p)
{
int *q = p;
printf(MF_F("freeing %d bytes @ %p\n"), *q, p);
free(p);
}
/* main program, allocate 1000 random sized buckets of memory,
* then deallocate them. */
int
main()
{
int i;
mf_t safe = NULL; /* initialize to NULL always */
for (i = 0; i < 10; i++) { /* several allocations */
int size = rand() & 1000 + sizeof(int);
printf("Allocating %d bytes @ %p\n", size, mf_save(&safe, malloc_wrapper(size)));
}
/* all are freed at the end of the work */
mf_free(&safe, free_wrapper);
}
(Note: I've written wrappers for malloc()
and free()
to insert traces on the amount of memory allocated/deallocated, but you can use malloc()
and free()
in their place.)
Makefile
This Makefile
allows you to build the test program.
targets = test_mf
test_mf_objs = test_mf.o mf.o
to_clean = test_mf $(test_mf_objs)
all: $(targets)
clean:
$(RM) $(toclean)
test_mf: $(test_mf_deps) $(test_mf_objs)
$(CC) $(CFLAGS) $(LDFLAGS) $($@_ldfl) -o $@ $($@_objs) $($@_libs)
The routines here are copyrighted, but you are free to use them under the BSD license.