My current (simplified) buffer API looks like this:
typedef struct {
size_t offset;
size_t size;
uint8_t *data;
} my_buffer;
// Writes an unsigned int 8 to the buffer
bool my_buffer_write_u8(my_buffer *buffer, uint8_t value) {
if (buffer->offset >= buffer->size) return false;
buffer->data[buffer->offset] = value;
buffer->offset;
return true;
}
However, after refreshing my knowledge about the strict aliasing rule in C I'm not so sure about this use case:
char string[32];
my_buffer buffer;
buffer.size = sizeof(string);
buffer.data = string; // <-- I think this violates the strict aliasing rule
buffer.offset = 0;
// the function calls access buffer.data which is defined to be `uint8_t *` and not `char *`
// in other words, I'm manipulating a `char *` through a `uint8_t *`:
// even though uint8_t is almost always unsigned char, it is nevertheless not the same as unsigned char
my_buffer_write_u8(&buffer, 'h');
my_buffer_write_u8(&buffer, 'e');
my_buffer_write_u8(&buffer, 'l');
my_buffer_write_u8(&buffer, 'l');
my_buffer_write_u8(&buffer, 'o');
my_buffer_write_u8(&buffer, '\0');
I think I should be using void *
in the buffer struct and use a (char *)
cast to access the underlying data:
typedef struct {
size_t offset;
size_t size;
void *data;
} my_buffer;
// Writes an unsigned int 8 to the buffer
bool my_buffer_write_u8(my_buffer *buffer, uint8_t value) {
if (buffer->offset >= buffer->size) return false;
unsigned char *data = (unsigned char *)buffer->data;
data[buffer->offset] = value;
buffer->offset;
return true;
}
Because char *
, unsigned char *
and signed char *
are always assumed to alias other datatypes.
The same cannot be said about uint8_t *
(according to the standard that is)
If CHAR_BIT
is 8
then this adjusted code with (void *)
should do exactly the same as with the uint8_t
version.
Now to the question: have I applied the rule of strict aliasing correctly?
CodePudding user response:
It would be UB if uint8_t
was different from unsigned char
.Assuming uint8_t
exists it is very unlikely because
- CHAR_BIT is at least 8, see http://port70.net/~nsz/c/c11/n1570.html#5.2.4.2.1p1
- unsigned char has no padding bits
- unsigned char is the smallest addressable unit
However, the standard does not explicitly require that uint8_t
is the same type as unsigned char
. Therefore it's rather implementation defined.
Consider applying solution from following thread to check it forementioned types are the same. How to assert two types are equal in c?
It is preferable to use char*
/unsigned char*
for accessing the data. However, if refactoring of the code would be cumbersome then just add a check if the types uint8_t
and unsigned char
are the same and reject compilation if not.