Does casting a pointer back and forth from size_t or uintptr_t break strict aliasing?
Asked Answered
S

1

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.

Skean answered 28/8, 2015 at 21:2 Comment(13)
Is size_t guaranteed to be wide enough to hold a pointer? I don't think so..Chrysa
@EugeneSh. Nope.Skean
Then it is not just about aliasing. Casting pointer to size_t may just truncate it.Chrysa
Do not use size_t! The correct type would be uintptr_t (You should know that, why else would you mention it?)Poultry
Wouldn't void * be better for a generic/opaque pointer anyway? Or am I missing something obvious here?Boscage
@Boscage void* is not good, because it makes type checking impossible. Think of what happens when you have two different handle types in your interface. If either one is void*, the compiler won't catch the error if the user mixes them up. With proper opaque pointers, the compiler will catch these errors.Corron
@cmaster, how does using uintptr_t help for type checking? I am lost.Vice
@JensGustedt It doesn't. That's why I suggest the use of proper opaque pointers instead of disguised, type-deleted pointers.Corron
@cmaster, but still void* is much better than uintptr_t or any other integer type.Vice
@JensGustedt Yes. Opaque pointers is better than void*, which is better than uintptr_t, which is better than size_t. Precisely in that order :-)Corron
See also question size_t vs intptr_t in C99Papillary
@JonathonReinhart It would help if you clarify that the code snippet above illustrate the original coding style in that library, not your proposed change.Papillary
@rwong: the phrase whose public API currently looks like does state that the shown code is the 'original' style.Vientiane
C
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.

Corron answered 28/8, 2015 at 21:12 Comment(4)
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.Poultry
@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.Corron
@Olaf uintptr_t is guaranteed to cast forth and back from/to a void *. Sure that works with any "pointer to an object"?Softspoken
@chux: I left out the intermediate step for brevity: 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).Poultry

© 2022 - 2024 — McMap. All rights reserved.