首页 > 解决方案 > 关于函数返回的 char 指针的最佳实践

问题描述

处理返回指向 C 字符串的 malloc 指针的函数时,最佳实践是什么?

这是一个例子:

FILE *f;
char *tmp;
for (i = 0; i <= 10; i++) {
    tmp = concat(fn, i);
    f = fopen(tmp, "w");
    free(tmp);
    // read f, do something useful ...
    fclose(f);
}

char* concat(char *a, int b)返回一个指向新 C 字符串的指针,其中包含 和 的a连接b

我不仅必须指定一个临时指针,然后将其传递给fopen,我还必须free(tmp)每次都这样做。我宁愿喜欢这样的东西:

FILE *f;
char *tmp;
for (i = 0; i <= 10; i++) {
    f = fopen(concat(fn, i), "w");
    // read f, do something useful ...
    fclose(f);
}

但这当然会导致内存泄漏。那么这里的最佳实践是什么?类似于concat(char *a, int b, char *result)where result 应该是生成的 C 字符串的预分配内存之类的东西?此解决方案有其缺点,例如result.

标签: cpointersscopereturn

解决方案


这两种方法都在工业中使用。在您的示例中,人们可以假设生成的文件名的最大大小并以这种方式使用本地数组:

for (int i = 0; i <= 10; i++) {
    char filename[1024];
    snprintf(filename, sizeof filename, "%s%d", fn. i);
    FILE *f = fopen(filename, "w");
    if (f != NULL) {
        // read f, do something useful ...
        fclose(f);
    } else {
        // report the error?
    }
}

请注意,可以使用 检测截断if (snprintf(filename, sizeof filename, "%s%d", fn. i) >= (int)sizeof filename)

如果不应该对文件名长度做出假设,或者文件名组合方法更复杂,则返回分配的字符串可能是更合适的选择,但也应该测试内存分配错误:

for (int i = 0; i <= 10; i++) {
    char *filename = concat(fn, i);
    if (filename == NULL) { 
       /* handle the error */
       ...
       // break / continue / return -1 / exit(1) ...
    }
    FILE *f = fopen(filename, "w");
    if (f == NULL) {
        /* report this error, using `filename` for an informative message */
    } else {
        // read f, do something useful...
        // keep `filename` available for other reporting 
        fclose(f);
    }
    free(filename);
}

如果您还没有准备好执行所有这些簿记,您可能应该使用具有更精细的对象生命周期管理或垃圾收集器的不同语言。


最后,使用 C99 复合文字,您可以定义concat以适合您的简化用例:

char *concat(char *dest, const char *s, int b) {
    sprintf(dest, "%s%d", s, b);
    return dest;
}
#define CONCAT(a, b) concat((char[strlen(a) + 24]){""}, a, b)

CONCATchar定义了一个适当大小的未命名的局部可变长度数组,并在其中构造了 stringa和 int的连接b。我将大小写更改为大写以强调在扩展中被评估两次的事实,a因此不应该是涉及副作用的表达式。

您可以在第二个代码片段中按预期使用此宏:

    FILE *f;
    char *tmp;
    for (i = 0; i <= 10; i++) {
        f = fopen(CONCAT(fn, i), "w");
        // read f, do something useful ...
        fclose(f);
    }

我可能不会推荐这种用法,但这只是我的意见。


推荐阅读