如何 return 一个 char** 作为函数参数

How to return a char** as a function argument

我有一个函数 returns 一个指向字符指针的指针 (char**)。该函数有 2 个参数

下面写了一些示例代码(为简单起见省略了大部分错误检查):

char** get_paths(int* num_paths, int* errno) {
    char* path1 = NULL;
    char* path2 = NULL;
    char** paths = NULL;

    if(errno == NULL) {
        printf("Set errno in case of error, but cannot dereference NULL pointer\n");
        goto exit;
    }

    path1 = calloc(1, strlen("foo") + 1);
    path2 = calloc(1, strlen("bar") + 1);

    strcpy(path1, "foo");
    strcpy(path2, "bar");

    *num_paths = 2;
    paths = calloc(1, *num_paths*sizeof(char *));
    paths[0] = path1;
    paths[1] = path2;

    *errno = 0;
exit:
    return paths;
}

int main(void) {
    char** paths = NULL;
    int num_paths = 0;
    int errno = 0;

    paths = get_paths(&num_paths, &errno);
    if(errno != 0) {
        return -1;
    }

    for(int i = 0; i < num_paths; i++) {
        printf("%s\n", paths[i]);
        free(paths[i]);
    }
    free(paths);
}

我遇到的问题是,如果 NULL 指针作为 errno 的参数传递,我无法设置错误代码。你可能会争辩说这是一个用户错误,但我仍然希望首先避免这种情况。

所以我的问题是:我可以重写我的 get_paths 函数,使其 returns 一个整数作为错误代码,而且 returns 一个 char** 通过函数不求助于 char*** 的参数,如下例所示:

int get_paths_3(char*** paths, int* num_paths) {
    char* path1 = NULL;
    char* path2 = NULL;

    path1 = calloc(1, strlen("foo") + 1);
    path2 = calloc(1, strlen("bar") + 1);

    strcpy(path1, "foo");
    strcpy(path2, "bar");

    *num_paths = 2;
    *paths = calloc(1, *num_paths*sizeof(char *));
    (*paths)[0] = path1;
    (*paths)[1] = path2;

    return 0;
}

不幸的是,你不能在 C 中混合 return 类型是一个可怕的错误,不应该这样做,你可以:

  • return你需要的指针并添加一个错误代码到一个变量
  • return 错误代码并添加指向变量的指针

两者都是有效的策略,我会选择与您的其余代码相匹配的策略以保持一致性。

这几乎是唯一可以使用“三星级”指针的情况。在 API 设计中,为错误代码保留 return 值是相当普遍的做法,因此这种情况并不少见。

有其他选择,但可以说它们也好不了多少。您可能会滥用 void* 可以转换为 to/from char** 的事实,但它不是更漂亮且类型安全性更低:

// not recommended

int get_paths_4 (void** paths, size_t* num_paths)
{
    char* path1 = calloc(1, strlen("foo") + 1);
    char* path2 = calloc(1, strlen("bar") + 1);

    strcpy(path1, "foo");
    strcpy(path2, "bar");

    *num_paths = 2;
    char** path_array;
    path_array= calloc(1, *num_paths*sizeof(char *));
    path_array[0] = path1;
    path_array[1] = path2;
    *paths = path_array;
    return 0;
}

...

void* vptr;
size_t n;

get_paths_4 (&vptr, &n);
char** paths = vptr;

for(size_t i=0; i<n; i++)
{
  puts(paths[i]);
}

一个更合理的替代方案可能是将所有参数包装到一个 struct 类型中并将该类型作为指针传递。