The majority of microcomputer C compilers have two signed integer types with the same size and representation, along with two such unsigned types. If int
is 16 bits, its representation will generally match short
; if long
is 64 bits, it will generally match long long
; otherwise, int
and long
will usually have matching 32-bit representations.
If, on a platform where long
, long long
, and int64_t
have the same representation, one needs to pass a buffer to three API functions in order (assume the APIs under the control of someone else and use the indicated types; if the functions could readily be changed, they could simply be changed to use the same type throughout).
void fill_array(long *dat, int size);
void munge_array(int64_t *dat, int size);
void output_array(long long *dat, int size);
is there any efficient standard-compliant way of allowing all three
functions to use the same buffer without requiring that all of the data
be copied between function calls? I doubt the authors of C's aliasing rules intended that such a thing should be difficult, but it is fashionable for "modern" compilers to assume that nothing written via long*
will be read via long long*
, even when those types have the same representation. Further, while int64_t
will generally be the same as either long
or long long
, implementations are inconsistent as to which.
On compilers that don't aggressively pursue type-based aliasing through function calls, one could simply cast pointers to the proper types, perhaps including a static assertion to ensure that all types have the same size. The problem is that if a compiler like gcc, after expanding out function calls, sees that some storage is written as long
and later read as long
, without any intervening writes of type long
, it may replace the later read with the value written as type long
, even if there were intervening writes of type long long
.
Disabling type-based aliasing altogether is of course one approach to making such code work. Any decent compiler should allow that, and it will avoid many other possible pitfalls. Still, it seems like there should be a Standard- defined way to perform such a task efficiently. Is there?
long long datll[size]; fill_array(MY_LLP_LP(datll), size);
and let the macro check/handle the conversions? – Lavationchar*
orvoid*
much more common? What library are you encountering with this profile? Also, wouldn't it be trivial to wrap the functions with a standard type? (ie: thunking) – Sistrunk#if
processing and_Static_assert
as able to insure the simple cast will suffice. – Lavationint64_t
are sometimes seen. It all depends on what you're trying to represent. – Lancelanceletmemmove
of the buffer followed by casting would be well-defined and optimized by "decent" compilers? – Leonardaleonardimemcpy
ormemmove
has no declared type, its Effective Type will become that of the source--a rule whose primary effect is to makememcpy
/memmove
useless for scrubbing effective types. Compilers would probably also be entitled to apply the same effective-type transference to something likefor (size_t i=0; i<size; i++) ((char*)buff)[i] = ((char*)buff[i]);
, though it's not quite clear what the phrase "through a character array" is really supposed to mean. – Washingtonmemmove
to copy an object to itself will indeed be stripped out by gcc while leaving the Effective Type of the buffer unchanged. – Washingtonunsigned char
to 16-bitunsigned int
, C89 implementations would be required to promote tosigned int
). – Washington