2
/*
//i comment this block, please see the updates
void* fun_one(char *buffer, long length)
{
    if(NULL == buffer)
        return  xxx; //return what value here
    ...
}

fun_two()
{
    ...
    fun_one(image->buffer, image->length);
    ...
}
*/

If the error happened, I do not hope to exit the program but return to fun_two(). What should I do? I know that return (void *)0; or return NULL;when succeed, but return what value when error?

links related: void *


update:

but what if ti's a thread function ,such as

int main()
{
    ...
    pthread_create(&id1, NULL, (void*)myThread1, &param_struct);
    ...
}

void* myThread1(void* param)
{
    struct len_str* img = (struct len_str*)param;
    if(NULL == img->buf)
        return xxx;    //return what value here
    if(0 == img->len)
        return xxx;
    ...
}

links related: pthread_create

the return value of myThread1() is void * what value should return if the error occur

Community
  • 1
  • 1
kgbook
  • 388
  • 4
  • 16
  • 3
    You can write `void * res = fun_one(...);` and test the value of `res`. – Mathieu Mar 01 '16 at 12:53
  • 2
    It's up to you. Why do you want to return a `void*`. I'd just return an `int`. – Jabberwocky Mar 01 '16 at 12:55
  • @MichaelWalz but what if it's a thread function, such as`pthread_create(&id1, NULL, (void*)myThread1, NULL);` – kgbook Mar 01 '16 at 13:59
  • If it's a thread function, pass some kind of structure in its argument and update that before returning: `pthread_create(&id1, NULL, (void*)myThread1, &some_kind_of_structure);`. Inside the function convert the `(void*)` to the proper type. – pmg Mar 01 '16 at 14:33

4 Answers4

3

Instead of returning a pointer, return a value of int type.

A return of 0 means all ok.
A return of 1 means NULL pointer passed in.
A return of 2 means invalid length.
...

int fun_one(char *buffer, long length) {
    if (buffer == NULL) return 1;
    if (length == 0) return 2;
    if (length < 0) return 3;
    /* ... */
    return 0;
}

For extra clarity (???), you can use enums

enum fun_one_errors {FO_OK, FO_NULL, FO_ZERO, FO_NEG};
return FO_ZERO;
pmg
  • 106,608
  • 13
  • 126
  • 198
2

You are struggling with the "design" of your interface. What do you need to pass to the function, what does it need to return and how do you signal an error.

Normally if you return a pointer, the pointer value zero (NULL) means an error. If you want to pass more information, you can use an auxiliary variable that you set to one of your error codes. This will be a global variable or a (pointer to) a variable:

int my_errcode;
...
if ((p=fun_one(buf, len))==NULL) printf("error %d\n",my_errcode);

As an alternative, you return the void * pointer as a parameter (you must use a double indirection now) and let the function return signal success, or failure and the type of failure:

if ((my_errcode=fun_one(buf, len, &p))!=0) printf("error %d\n",my_errcode);

So it is up to you...

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • yes, your answer is absolutely right.however, if the function is a thread function, the type of the return value is `void *` – kgbook Mar 01 '16 at 14:07
0

In your thread function, you would dynamically allocate a variable to store the return value. You would set that value and return the pointer.

Then when you call pthread_join, you'll receive the pointer to that variable, which you would then cast and dereference to read the value, and free it when you're done.

void* myThread1(void* param)
{
    int *rval = malloc(sizeof(int));
    if (rval == NULL) {
        perror("malloc failed");
        exit(1);
    }
    struct len_str* img = (struct len_str*)param;
    if(NULL == img->buf) {
        *rval = 0;
        return rval;
    }
    if(0 == img->len) {
        *rval = 1;
        return rval;
    }
    ...
}


int main()
{
    ...
    pthread_create(&id1, NULL, (void*)myThread1, &param_struct);
    ...
    void *rval;
    pthread_join(id1, &rval);
    printf("rval = %d\n", *(int *)rval);
    free(rval);
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • the prototype: `int pthread_join(pthread_t thread, void **retval);`,Is `void *rval;pthread_join(id1, &rval);` equal to `void **rval;pthread_join(id1, rval);`?thanks – kgbook Mar 02 '16 at 01:04
  • @CoryKang It is *not* the same. In the first case, you're passing in the *address* of a variable which `pthread_join` then dereferences and populates. In the second case, you're passing the *value* of a variable which has not been initialized, so `pthread_join` will attempt to use that uninitialized variable as an address and dereference it, most likely causing a segfault. – dbush Mar 02 '16 at 01:24
  • `void **rval = malloc(4);pthread_join(id1, rval);`,then it's the same? – kgbook Mar 02 '16 at 01:31
  • @CoryKang No, still not good because you're assuming a `void *` is 4 bytes. It might, or it might not. You'd have to call `malloc(sizeof(void *))`. That would work, but then you would have to `free(*rval)` to free the memory the thread created and `free(rval)` to free the memory the main thread created. You're better of declaring a `void *` and passing the address as I demonstrated. – dbush Mar 02 '16 at 01:41
  • http://stackoverflow.com/questions/399003/is-the-sizeofsome-pointer-always-equal-to-four; Oh,i see.thanks – kgbook Mar 02 '16 at 02:04
0

Well, when returning a pointer you have normally the problem of what to do if the function execution resulted in an error?

The classical approach is to reserve a special value (NULL, or 0 in C++) to represent this kind of error, and use a global to represent the different error values you can have.

But, what if you want a reentrant module to be able of multithreading? Well, the pointers have the problem that everywhere they point to is a possible source of trouble if that place happens to be occupied by a non-useless variable.

But we have a different approach. Just create dumb variables (not used for anything else) to hold the addresses of nothing. This way you can reserve several pointer values (the addresses of those variables) to represent the proper error codes. As the places they have been located by the compiler cannot be used for anything else, they can be used to hold (not to store) the address of something special to you.

int error_001_placeholder;
int error_002_placeholder;
int error_003_placeholder;

...

if (condition_1) return &error_001_placeholder;
if (condition_2) return &error_002_placeholder;
if (condition_3) return &error_003_placeholder;

Then, you'll be able to check for different invalid return pointers, just by checking if they belong to this set of pointers.

Even more, if you want to do generic error check, you can reserve a global array, and return individual cells for different errors, finally to generic check if the returned value is an error, you can just check if returned_value >= errors_array && returned_value < errors_array + erros_array_size.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31