Strict aliasing rule: pointer to void vs. pointer to char and transitivity

X

Xavier Roche

Hi folks!

My understanding of the aliasing rules is that a pointer to a char may
alias any other pointer type. The following trivial example is therefore
defined in C:

static void printBytes(const char *bytes, size_t size) {
size_t i;
for(i = 0 ; i < size ; i++) {
printf("byte [%zu] == %d\n", i, bytes);
}
}

....
const double foo = 42.0;
printBytes((char*) &foo, sizeof(foo)); /* print bytes of foo */

However, when using a pointer to void to transition to a pointer to a
char, is the aliasing rule still unviolated ?

Ie. the following code, which does not require any cast in user code,
but casts inside the function:

static void printBytes(const void *ptr, size_t size) {
const char *const bytes = (const char*) ptr;
size_t i;
for(i = 0 ; i < size ; i++) {
printf("byte [%zu] == %d\n", i, bytes);
}
}

....
const double foo = 42.0;
printBytes(&foo, sizeof(foo));

My understanding is that a "pointer to void" may be casted to anything,
but aliasing rules apply on "both sides" to the pointer -- ie. when
casting from A* to void*, then from the same void* to B*, the aliasing
rules between A* and B* apply.

Am I correct ?

(This would allow to write functions such as memset() in a non-undefined
behavior way)
 
K

Kaz Kylheku

Hi folks!

My understanding of the aliasing rules is that a pointer to a char may
alias any other pointer type.

This is necessary in order to tell a credible story about how uses of functions
like memcpy and memmove are well-defined ("they just are" not being a credible
story), and to allow such functions to be written in C.
However, when using a pointer to void to transition to a pointer to a
char, is the aliasing rule still unviolated ?

It doesn't matter how you the pointer to char is obtained, as long as every
step in the sequence of conversions is well-defined.
 
M

Malcolm McLean

This is necessary in order to tell a credible story about how uses of
functions like memcpy and memmove are well-defined ("they just are" not
being a credible story), and to allow such functions to be written in C.
Sometime you need to treat an object of unknown type as a buffer of bytes.
e.g. to sort it, copy it, compress it, take a hash of it, and so on. A fairly
limited set of operations, but just big enough that you can't expect library
functions to cover every eventuality.
So you have to be able to convert from void * to unsigned char *, because
void * cannot be dereferenced.
 
J

James Kuyper

Hi folks!

My understanding of the aliasing rules is that a pointer to a char may
alias any other pointer type. The following trivial example is therefore
defined in C:

static void printBytes(const char *bytes, size_t size) {
size_t i;
for(i = 0 ; i < size ; i++) {
printf("byte [%zu] == %d\n", i, bytes);
}
}

...
const double foo = 42.0;
printBytes((char*) &foo, sizeof(foo)); /* print bytes of foo */


Just a matter of terminology: that's an example of using char to alias
double; it's not an example of using char* to alias double*. You'd need
one additional level of indirection to alias a pointer type, and such
aliasing would violate the aliasing rules.
However, when using a pointer to void to transition to a pointer to a
char, is the aliasing rule still unviolated ?
Ie. the following code, which does not require any cast in user code,
but casts inside the function:

static void printBytes(const void *ptr, size_t size) {
const char *const bytes = (const char*) ptr;
size_t i;
for(i = 0 ; i < size ; i++) {
printf("byte [%zu] == %d\n", i, bytes);
}
}

...
const double foo = 42.0;
printBytes(&foo, sizeof(foo));

My understanding is that a "pointer to void" may be casted to anything,
but aliasing rules apply on "both sides" to the pointer -- ie. when
casting from A* to void*, then from the same void* to B*, the aliasing
rules between A* and B* apply.


It's A and B that the aliasing rules refer to, not A* and B*. Yes, the
fact that "void*" was used in an intermediate step is irrelevant to the
application of those rules.
Am I correct ?
(This would allow to write functions such as memset() in a non-undefined
behavior way)

Correct. All of the mem*() functions can be implemented using ordinary C
functions that work this way, and have well-defined behavior when passed
arguments meeting the standard's requirements for those functions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,763
Messages
2,569,560
Members
45,035
Latest member
HoTaKeDai

Latest Threads

Top