Reputation: 80
I have a function that returns a pointer to pointers of chars (char**
). The function takes 2 arguments
int* num_paths
: a pointer to an integer to indicate the number of strings to be returned.int* errno
: a pointer to an integer to indicate an error code. This can contain different values, so I cannot simply check if NULL
is returned in case of error.Some example code is written below (with the majority of error checks omitted for simplicity):
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);
}
The problem I have with this is that I can't set the error code in case a NULL pointer is passed as argument for errno
. You could argue that this is a user error, but I would still like to avoid this situation in the first place.
So my question is: can I rewrite my get_paths
function such that it returns an integer as error code, but also returns a char**
through the function arguments without resorting to char***
like in the following example:
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;
}
Upvotes: 0
Views: 542
Reputation: 213513
This is pretty much the only case where "three star" pointers are fine to use. It's fairly common practice in API design to reserve the return value for error codes, so this situation isn't uncommon.
There are alternatives, but they are arguably not much better. You could abuse the fact that void*
can be converted to/from char**
but it isn't much prettier and less type safe:
// 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]);
}
A more sound alternative might be wrap all your parameters into a single struct
type and pass that one as a pointer.
Upvotes: 1
Reputation: 341
Unfortunately you cannot mixing return types in C is a terrible mistake and should not be done, you can either:
Both are valid strategies and I'd pick the one that matches the rest of your code to have consistency.
Upvotes: 0