Or maybe, I shouldn't cast. Here's what I'm doing:
I'm writing a piece of code that links a Linux device driver to a higher level library. The authors of the library use 开发者_StackOverflowvoid *
(under a new name via typedef
) to store handles to an implementation specific object that describes a communication channel.
The driver I want to connect with the library uses int
to store handles to its channels (because they are file descriptors as returned by calls to open()
). So, in my code, I get void *
passed in from the library and need to call stuff from the driver using an int
and vice versa. I. e.:
// somewhere in the library ...
typedef void* CAN_HANDLE;
// ... in my code
CAN_HANDLE canOpen_driver(s_BOARD *board)
{
int fd;
// ...
fd = open(busname, O_RDWR);
// ...
return (CAN_HANDLE) fd; // <-- not safe, especially not when converting back.
}
The adapters that others have written actually store some struct etc. somewhere and just cast between pointers, so no size issues arise. In my case, I don't really want to manage file descriptors, as the OS already does.
On my PC, I think the pointer is larger than the int
, so I could bit-twiddle my way out of this, but the code goes into embedded systems, too, and I'm not experienced enough to make any assumptions about the size of types on those machines.
Edit: Of course you don't need a struct, you can just allocate memory for a plain int.
CAN_HANDLE canOpen_driver(s_BOARD *board)
{
int *fd = malloc(sizeof(int));
if (fd)
{
// ...
*fd = open(busname, O_RDWR);
// ...
return (CAN_HANDLE) fd;
}
// failure
return NULL;
}
This assumes there's a matching call to clean up. Something like:
void canClose_driver(CAN_HANDLE handle)
{
int *fd = handle;
free(fd);
}
Depending on the architecture, you might get away with that. If I understand correctly, the driver never actually uses the void* that you provide to it. It simply stores it to pass it back to your code later on.
Based on that assumption, as long as sizeof(void*) >= sizeof(int), it will be safe to cast between those types because you are sure that it is really a int.
If you cannot guarantee the size condition, or do not want to rely on a hack, you should allocate memory for the int and return the address of that memory. You might use malloc() or allocate a int in a fixed-size array, for example. The downside is that you will need to free that memory when it is no longer needed. I imagine that the driver has some kind of notification that signals your code when the data structure is no longer needed.
Generally on embedded systems, the following CPU models are most common:
Data bus Address bus
8 bit 16 bit
8 bit 16+8 bit (banking)
16 bit 16 bit
16 bit 16+8 bit (banking)
32 bit 32 bit
Generally, the address bus will always be >= than the data bus. I can't think of any CPU where the data bus would be larger than the address bus.
Here's a somewhat dirty trick that may or may not solve the issue:
typedef union
{
CAN_HANDLE handle;
long value;
} CAN_HANDLE_t;
This should be fairly portable, even though you will likely have to adapt this union to the specific system (far pointers etc).
精彩评论