По стандарту C допускается приводить указатель на структуру к указателю типа первого члена этой структуры. Однако разрешено ли поступать наоборот?
Рассмотрим следующий пример:
//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
}
Могу ли я безопасно привести HAL_TEXTURE* (который является первым членом D3D10_TEXTURE и OGL_TEXTURE) к указателю охватывающего типа?
@ i486: Это чрезвычайно важно для таких случаев использования, как объектная система CPython, где все объекты начинаются с PyObject в качестве первого члена, а указатели PyObject * необходимо приводить к указателям на включающий тип структуры и обратно.
Здесь получить доступ к texture->w достаточно просто с помощью HAL_TEXTURE *, но если вы хотите получить доступ к srv или id, вам нужно сначала получить D3D10_TEXTURE * или OGL_TEXTURE *.
Разрешено получить указатель на любое место объекта из указателя на любое другое место в объекте.





Да. Согласно стандарту ISO C (рабочий проект C2x от 2019 года) (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2347.pdf), раздел 6.7.2.1 §15 :
- Внутри структурного объекта члены, не являющиеся битовыми полями, и единицы, в которых находятся битовые поля, имеют адреса увеличиваются в том порядке, в котором они объявлены. Указатель на объект структуры, преобразованный соответствующим образом, указывает на свой начальный элемент (или, если этот элемент является битовым полем, то на единицу измерения в где он находится), и наоборот. Внутри объекта структуры могут быть безымянные отступы, но не в его начале.
@EricPostpischil спасибо, я обновлю ссылку до актуального стандарта.
Это разрешено. Для этого и нужна часть «и наоборот». §6.5 параграф 7 не запрещает доступ к членам; выражение доступа к члену будет иметь правильный тип.
Спасибо @user2357112, я отменю редактирование.
В частности, структура является «агрегированным типом», и раздел 6.5 §7 разрешает доступ к члену структуры по lvalue со стороны типа структуры, имеющего тот же тип, что и этот член, в качестве одного из его объектов. Это может показаться сумасшедшим, но причина этого правила в том, что компиляторам не разрешено предполагать, что некоторые int остаются неизменными, когда код обновил структуру только путем доступа к указателю, в случае, если эта структура содержит int член. Это будут различные «правила псевдонимов указателей».
Ты можешь это сделать, но зачем? Сложно написать
xxx->w?