C语言 如何正确释放传入可变参数函数的位置不对的变量?

gorkyyrv  于 2023-02-07  发布在  其他
关注(0)|答案(3)|浏览(106)

请考虑以下示例

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_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);

}

在不对传递变量的数目做任何假设的情况下,释放错位空间的最有效方法是什么?

fdx2calv

fdx2calv1#

宏可以发送一个伪参数和一个最终的NULL,然后在函数中使用一个va_list

#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);
}

或者,如果你愿意,你也可以把一个复合字面值传递给一个期待指针数组的函数(优点是编译器可以进行类型检查):

#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;
}

如果你想隐藏复合文本的构造,代价是用宏混淆代码:

#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;
}
hrirmatl

hrirmatl2#

您需要使用一个sentinel value来标记变量参数列表的结尾:

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);
}

用法:

free_all(NULL); // no-op
free_all(string1, string2, string2, NULL);

唯一需要注意的是,你不能用这种方式传递NULL指针,因为NULL表示变量参数列表的末尾。

free_all(string1, NULL, string2);

不会释放string2

bz4sfanl

bz4sfanl3#

你必须迭代参数并一个接一个地释放它们。我会这样做:

#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);
}

相关问题