According to the C Standard, it is allowed to cast a pointer to a struct to a pointer of the type of the first member of this struct. However, is it allowed to do the opposite?
Consider the following example:
//texture.h
typedef struct HAL_TEXTURE {
size_t w;
size_t h;
} HAL_TEXTURE;
HAL_TEXTURE* CreateTexture(size_t w, size_t h, void* data);
void BindTexture(HAL_TEXTURE* texture);
// d3d10/texture.c linked on windows
typedef struct D3D10_TEXTURE { // not visible outside of this translation unit
HAL_TEXTURE agnostic;
ID3D10ShaderResourceView* srv;
} D3D10_TEXTURE;
HAL_TEXTURE* CreateTexture(size_t w, size_t h, void* data) {
D3D10_TEXTURE* texture = calloc(...);
//...//
return (HAL_TEXTURE*)texture; // ok, according to standard
}
void BindTexture(HAL_TEXTURE* texture) {
D3D10_TEXTURE* tex = (D3D10_TEXTURE*)texture; // is this allowed?
//...//
//access D3D10 specific stuff here
}
// ogl/texture.c linked on linux
typedef struct OGL_TEXTURE { // not visible outside of TU
HAL_TEXTURE agnostic;
GLint id;
} OGL_TEXTURE;
HAL_TEXTURE* CreateTexture(size_t w, size_t h, void* data) {
OGL_TEXTURE* texture = calloc(...);
//...//
return (HAL_TEXTURE*)texture;
}
void BindTexture(HAL_TEXTURE* texture) {
OGL_TEXTURE* tex = (OGL_TEXTURE*)texture;
//...//
//access OpenGL specific stuff here
}
Can I safely cast HAL_TEXTURE*
(which is the first member of D3D10_TEXTURE
and OGL_TEXTURE
) to a pointer of the enclosing type?
Yes. According to the ISO C standard (C2x working draft from 2019) (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2347.pdf), section 6.7.2.1 §15:
- Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. 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.