6

I'm proposing a change to a library whose public API currently looks like this:

typedef size_t enh;  /* handle */

int en_open(enh *handle)
{
    struct internal *e = malloc(...);
    *handle = (enh)e;
    return 0;
}

int en_start(enh handle)
{
    struct internal *e = (struct internal*)handle;
    return do_something(e);
}

Does this usage, casting back and forth to size_t break strict aliasing?

For the record, I'm proposing a typical opaque forward declaration of struct internal in the public API, as shown on this Programmers.SE question about the same code.

Community
  • 1
  • 1
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328

1 Answers1

8

Aliasing is about two pointers of different type being used to access the same bytes. This is not the case in your code. When you access the data members behind the handle, you always do it via a pointer of type struct internal*. So no harm here.

The only questionable thing in your code is, that you are using size_t to pass the pointer. Afaik, the standard does not guarantee that you can safely cast a pointer to size_t and back, even though any sane implementation will allow it. The correct integer type choice would be uintptr_t, but you don't even need that:

I think, you should just use an opaque pointer in the interface. I. e., just put the declaration

typedef struct internal internal;

into your public header and keep the corresponding

struct internal {
    ...
}

private (replacing internal with a sensible public name, of course). The public functions can then simply be declared as:

int en_open(internal** outHandle);
int en_close(internal* handle);

That way, you get perfect type checking in the client code, and avoid the need for any casts.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • `uintptr_t` is guaranteed to cast forth and back from/to a pointer to an object. However, you are right to avoid casting where possible. – too honest for this site Aug 28 '15 at 21:15
  • @Olaf Yes, of course, I ignored that in my first draft because the cast to integer is not needed, so I didn't see a need to point out the correct integer type to cast to as well. I added a note about `uintptr_t` now, though. – cmaster - reinstate monica Aug 28 '15 at 21:26
  • @Olaf `uintptr_t` is guaranteed to cast forth and back from/to a `void *`. Sure that works with any "pointer to an object"? – chux - Reinstate Monica Aug 28 '15 at 21:48
  • 1
    @chux: I left out the intermediate step for brevity: [6.3.2.3p1](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p1). If you want to be safe, you can cast twice. It does work without for gcc-arm-none-eabi (with most warnings enabled), however. And if I look at paragraphs 5 and 6, the cast itself is also fine to me. (NOte that in C functions are **no** objects). – too honest for this site Aug 28 '15 at 21:53