Consider the following example
char** make_copy(int argc, char** argv){
char** new_argv = malloc((argc 1) * sizeof *new_argv);
for(int i = 0; i < argc; i)
{
size_t length = strlen(argv[i]) 1;
new_argv[i] = malloc(length);
memcpy(new_argv[i], argv[i], length);
}
new_argv[argc] = NULL;
return new_argv;
}
Above function malloc memory to make copies
void free_space(**args, ...){
// how to do ?
}
int main(){
char** string1 = make_copy(); # make a copy of some string
char** string2 = make_copy(); # make a copy of another string
char** string3 = make_copy(); # make a copy of third string
free_space(string1, string2, string3, NULL);
}
without make any assumption of number of passing variable, what is the most efficient way of freeing malloced space?
CodePudding user response:
A macro can send a dummy first parameter and a final NULL
, then just use a va_list
inside your function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define free_all(...) free_all(0, __VA_ARGS__, NULL)
void (free_all)(int dummy, ...)
{
va_list args;
va_start(args, dummy);
while (1)
{
char **ptr = va_arg(args, char **);
if (ptr == NULL)
{
break;
}
while (*ptr != NULL)
{
free(*ptr);
ptr ;
}
}
va_end(args);
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all(a, b, c);
}
or if your prefer you can pass a compound literal to a function expecting an array of pointers (with the advantage that the compiler can do type checking):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void free_all(char **list[])
{
while (*list != NULL)
{
char **ptr = *list;
while (*ptr != NULL)
{
free(*ptr);
ptr ;
}
list ;
}
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all((char **[]){a, b, c, NULL});
return 0;
}
if you want to hide the construction of the compound literal at the cost of obfuscating the code with a macro:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define free_all(...) free_all(((char **[]){__VA_ARGS__, NULL}))
void (free_all)(char **list[])
{
while (*list != NULL)
{
char **ptr = *list;
while (*ptr != NULL)
{
free(*ptr);
ptr ;
}
list ;
}
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all(a, b, c);
return 0;
}
CodePudding user response:
What you want is to use a sentinel value to mark the end of the variable arguments list:
void free_one(void *ptr) {
// logic to free a pointer
}
void free_all(void *ptr, ...) {
// short-circuit free_all(NULL)
if (!ptr) return;
va_list args;
free_one(ptr);
va_start(args, ptr);
while (true) {
void *arg = va_arg(args, void *);
// stop on the first NULL
if (!arg) break;
free_one(arg);
}
va_end(args);
}
Usage:
free_all(NULL); // no-op
free_all(string1, string2, string2, NULL);
The only caveat is that you can't pass a NULL
pointer this way, since NULL
denotes the end of the variable arguments list. So:
free_all(string1, NULL, string2);
Will not free string2
.
CodePudding user response:
You have to iterate over arguments and free them one by one. I would do just:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char** make_copy(int argc, char** argv) {
char** new_argv = malloc((argc 1) * sizeof *new_argv);
for (int i = 0; i < argc; i) {
size_t length = strlen(argv[i]) 1;
new_argv[i] = malloc(length);
memcpy(new_argv[i], argv[i], length);
}
new_argv[argc] = NULL;
return new_argv;
}
void free_one(char** ptr) {
for (char** i = ptr; *i; i) {
free(*i);
}
free(ptr);
}
void free_space(char** ptr, ...) {
va_list va;
va_start(va, ptr);
while (ptr) {
free_one(ptr);
ptr = va_arg(va, char**);
}
va_end(va);
}
int main() {
char** string1 = make_copy(1, (char*[]){"1a"});
char** string2 = make_copy(2, (char*[]){"2a", "2b"});
char** string3 = make_copy(3, (char*[]){"3a", "3b", "3c"});
free_space(string1, string2, string3, NULL);
}