Использование переопределения пустого указателя на указатель на анонимную структуру?
Я работал с кодом, связанным с драйвером UEFI, и наткнулся на это:
/* EFI headers define EFI_HANDLE as a void pointer, which renders type
* checking somewhat useless. Work around this bizarre sabotage
* attempt by redefining EFI_HANDLE as a pointer to an anonymous
* structure.
*/
#define EFI_HANDLE STUPID_EFI_HANDLE
#include <ipxe/efi/Uefi/UefiBaseType.h>
#undef EFI_HANDLE
typedef struct {} *EFI_HANDLE;
Полный исходный код находится по этому пути http://dox.ipxe.org/include_2ipxe_2efi_2efi_8h_source.html
Это моя первая встреча с анонимной структурой, и я не мог разобрать логику переопределения void *
на указатель на анонимную структуру. Какого рода хак, на который намекает "попытка саботажа в Биззар"?
2 ответа
Библиотека использует информацию, скрывающуюся от внутреннего объекта данных за адресом, хранящимся в EFI_HANDLE. Но при этом они делают код более восприимчивым к случайным ошибкам.
В С, void*
прозрачно приведен к любому другомуvoid*
неконстантный тип указателя данных без предупреждения (это по языковой схеме).
Использование не пустого типа указателя обеспечивает EFI_HANDLE
используется только там, где EFI_HANDLE
принадлежит. Проверка типа компилятора пинает вас в пах, когда вы передаете его в другое место, которое не EFI_HANDLE
, а скорее указатель на что-то другое.
Пример: как void*
, это будет скомпилировано без предупреждения или ошибки
#include <string.h>
#define EFI_HANDLE void*
int main()
{
EFI_HANDLE handle = NULL;
strcpy(handle, "Something");
}
Изменение псевдонима на:
typedef struct {} *EFI_HANDLE;
будет пожинать последующую ошибку "несовместимого типа указателя" во время компиляции.
Наконец, как анонимная структура, нет никакого бессмысленного имени тега структуры, добавляющего к уже загрязненному пространству имен, которое вы можете использовать (случайно или неосторожно).
Это не анонимная структура, а структура без тега.
Анонимная структура может существовать только как член другой структуры,
и он также не должен иметь тег1.
Определение структуры без каких-либо членов не допускается. Код, который вы просматриваете, использует расширение компилятора, которое позволяет это.
Библиотека делает это, чтобы скрыть определение структуры от пользователя, сохраняя при этом безопасность типов.
Однако есть гораздо лучший способ сделать это. Если у вас есть скрытое определение структуры, вы все равно можете определить непрозрачный указатель на него, который имеет тип, поэтому он безопасен для типа:
struct hidden //defined in a file and not exposed
{
int a;
};
void Hidden( struct hidden* );
void Other( struct other* );
struct hidden* a = NULL; //doesn't see the definition of struct hidden
Hidden( a ); //it may be used
Other( a ); //compiler error
1 (Цитируется из: ИСО / МЭК 9899:201x 6.7.2.1 Спецификация структуры и объединения 13)
Элемент без имени, спецификатор типа которого является структурным спецификатором без тега, называется анонимной структурой; неназванный член, спецификатор типа которого является спецификатором объединения без тега, называется анонимным объединением. Члены анонимной структуры или союза считаются членами содержащей структуры или союза. Это применяется рекурсивно, если содержащая структура или объединение также являются анонимными