Accessing pointer variable as a pointer to a diffe

2019-08-25 07:30发布

问题:

Is it good practice to access a pointer variable by dereferencing a pointer to a pointer, which points to a different type or void? Could this break strict aliasing rules? C and C++ have some differences in aliasing rules. In this question we focus on C. The other question considering C++ can be found here. In the following example a double* is accessed as a void*.

int create_buffer(void** ptr, ...)
{
    *ptr = malloc(...);
    ...
}

int main(void)
{
    double* buffer;

    // The problematic code is here, double**
    // is coerced to void**, which is later
    // dereferenced by the function
    create_buffer((void**)&buffer, ...);
    ...
}

Is the following any better:

// keeping void** just as an indicator in the interface
// that the pointer is pointing to a pointer to any type
// it could be replaced by just void*
int create_buffer(void** ptr, ...)
{
    void* result = malloc(...);
    memcpy((void*)ptr, &result, sizeof result);
}

回答1:

Not answering your question, but you could manoeuvrer around the uncertainties you mention by just doing well defined stuff like:

int main(void)
{
  double* buffer;

  {
    void * pv;
    create_buffer(&pv, ...);
    buffer = pv; /* C does not need any cast here. */
  }

  ...


回答2:

I would rather write it like this, assuming you use the "int" to return some useful information to the caller that it can't do without:

void *create_buffer(size_t n, int *otherInfo) {
    void *ptr = malloc(n);
    *otherInfo = 0;
    if (ptr) {
        // Do whatever other initialization is expected
        // memset(ptr, 0, n);
    } else {
        *otherInfo = -1;
    }
    return ptr;
}

int main(void)
{
    double* buffer;
    int code;

    /* cast is not required by language or compiler, 
       but I still find it useful when reading */
    buffer = (double *)create_buffer(SIZE_REQUIRED, &code);

    /* equality testing can be done either way. This way here,
       if you ever forget a =, will never compile anywhere. Nowadays
       many compilers are smart enough to catch it on their own anyway */
    if (NULL == buffer) {
        // Handle out of memory error or other problem
        switch(code) {
            case -1:
                fprintf(stderr, "Out of memory\n");
            ...
            default:
                fprintf(stderr, "Unexpected error %d\n", code);            
                break;
        }
    }
}