I was working with UEFI driver-related code, and I came across this:
/* 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;
The full source code is in this path http://dox.ipxe.org/include_2ipxe_2efi_2efi_8h_source.html
This is my first encounter with anonymous structure, and I couldn't make out the logic of redefining a void *
to a pointer to an anonymous structure. What kind of a hack the "bizzare sabotage attempt" hints at?
Why do we use a void pointer in C programs? We use the void pointers to overcome the issue of assigning separate values to different data types in a program. The pointer to void can be used in generic functions in C because it is capable of pointing to any data type.
A void pointer is a pointer that can point to any type of object, but does not know what type of object it points to. A void pointer must be explicitly cast into another type of pointer to perform indirection. A null pointer is a pointer that does not point to an address.
A void pointer cannot be dereferenced. We get a compilation error if we try to dereference a void pointer. This is because a void pointer has no data type associated with it. There is no way the compiler can know what type of data is pointed to by the void pointer.
In the above example, an int variable x is declared first. The void* pointer in C++ can point to any data type and typecast that data type without explicitly typecasting. It does not have any return value. A void* pointer can point to an int, float, or char and typecasts to that specific data type.
The library is using information hiding on the internal data object behind the address held in an EFI_HANDLE. But in doing so, they're making the code more susceptible to accidental bugs.
In C, void*
is transparently cast to any other non-void*
non-const data pointer type without warning (it's by language design).
Using a non-void pointer type ensures an EFI_HANDLE
is only used where EFI_HANDLE
belongs. The compiler's type-checking kicks you in the groin when you pass it somewhere else that isn't EFI_HANDLE
, but rather a pointer to something else.
Ex: As void*
, this will compile without warning or error
#include <string.h>
#define EFI_HANDLE void*
int main()
{
EFI_HANDLE handle = NULL;
strcpy(handle, "Something");
}
Changing the alias to:
typedef struct {} *EFI_HANDLE;
will reap the ensuing "incompatible pointer type" compile-time error.
Finally, as an anonymous struct, there is no pointless structure tag name adding to the already-polluted name space that you can use (accidently or nefariously).
That isn't an anonymous structure, but a struct without a tag.
An anonymous structure can only exist as a member of another struct,
and it must also not have a tag1.
Defining a struct without any members is not allowed. The code you're looking at is using a compiler extension that permits this.
The library is doing this to hide the definition of the structure from the user, while maintaining type safety.
However there is a much better way to do this. If you have a hidden structure definition, you can still define an opaque pointer to it, that has a type, so it is type safe:
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 (Quoted from: ISO/IEC 9899:201x 6.7.2.1 Structure and union specifiers 13)
An unnamed member whose type specifier is a structure specifier with no tag is called an
anonymous structure; an unnamed member whose type specifier is a union specifier with
no tag is called an anonymous union. The members of an anonymous structure or union
are considered to be members of the containing structure or union. This applies
recursively if the containing structure or union is also anonymous
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With