2

In the following setup

typedef struct {
    unsigned char data[64];
} mystruct;

int myfunc(mystruct* arg); // fills arg with data

is it safe to call myfunc with a pointer to a 64 byte array? E.g.

unsigned char buffer[64];
myfunc((mystruct*) buffer)

In my concrete application I am using a JNI direct ByteBuffer which should be filled from myfunc.

unsigned char* dbbuffer = (unsigned char*) (*env)->GetDirectBufferAddress(env, jbuffer);

If the cast is not safe, I would have to create a mystruct, call myfunc and then memcopy to dbbuffer, which I would like to avoid.

Lundin
  • 195,001
  • 40
  • 254
  • 396
Manuel Schmidt
  • 2,429
  • 1
  • 19
  • 32

1 Answers1

2

Technically it works and you can use it. As pointed in comments the relevant part of the ANSI standard is:

6.7.2.1: Structure and union specifiers

... A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

The strict aliasing does not matter in this case. Strict aliasing rule specifies in which circumstances a value of some type can be changed by changing a value of another type. The main interest of this rule are scalar types like int and float. In your particular case it is evident that by changing a member of a struct (unsigned char []) you change the whole structure and vice versa.

This case is covered by the 5th subcase of the strict aliasing rule. I quote the whole part for the sake of completeness:

6.5 Expressions, p.7

An object shall have its stored value accessed only by an lvalue expression that has one of the following types (The intent of this list is to specify those circumstances in which an object may or may not be aliased):

— a type compatible with the effective type of the object,

— a qualified version of a type compatible with the effective type of the object,

— a type that is the signed or unsigned type corresponding to the effective type of the object,

— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or

— a character type

where the definition of aggegate types is in:

6.2.5 Types, p.21

Arithmetic types and pointer types are collectively called scalar types. Array and structure types are collectively called aggregate types.

Marian
  • 7,402
  • 2
  • 22
  • 34
  • Note that it slips through strict aliasing just because the struct contains the exact type `unsigned char [64]`, same type as the effective type of the original array. Had the original array been of a different size than the one in the struct, I believe this would be a strict aliasing violation. – Lundin Mar 02 '18 at 07:56
  • In C stanadard gibberish: the struct is an aggregate that includes one of the aforemntioned types, namely a type compatible with the effective type of the object. Two arrays are compatible if they have both the same type specifier and same size. – Lundin Mar 02 '18 at 07:59
  • @Lundin Sure. However, arrays decay to pointers in majority of circumstance. An example where array members can't be aliased will look rather artificially. – Marian Mar 02 '18 at 08:17
  • 1
    Array decay has nothing to do with declared types. An example where array members can't be aliased would simply be `unsigned char buffer[63]; myfunc((mystruct*) buffer)`. Since `unsigned char[63]` and `unsigned char[64]` are not compatible types. (C11 6.2.7 which for the case of arrays points at 6.7.6.2/6) – Lundin Mar 02 '18 at 08:35
  • My point is that you can not assign the whole array to another array (only as part of a larger aggregate type). In a real code any assignment between `char[63]` and `char[64]` will go through altering individual `char`s. – Marian Mar 02 '18 at 08:42
  • 1
    Given `typedef struct { unsigned char data[64]; } mystruct;` and `typedef struct { unsigned char data[64]; } mystruct64;`, you can assign the whole arrays through `mystruct ms; mystruct64 ms64; ... ms = *(mystruct*)&ms64;`. This is perfectly valid and well-defined code. – Lundin Mar 02 '18 at 08:58