casting a pointer to integer issues warning on 64bit arch
I'm writing a linux kernel module that makes use of the exported symbol open_exec
struct file *open_exec(const char *name)
It returns a pointer, and I can check for an error with the IS_ERR
macro:
if (IS_ERR(file))
return file;
During compile time, I get this warning:
warning: return makes integer from pointer without a cast
This is because my function here returns an integer. If I try to cast it:
return (int) file;
I don't get a warning on my 32bit machine, but I do on my 64bit machine:
warning: cast from pointer to integer of different size
This is because the sizeof
of an int and a pointer are the same on 32bit, but they differ on a 64bit machine.
Casting it or not, the code appears to w开发者_开发百科ork. I'd just like to get rid of the warning.
How do I properly cast a pointer to an integer and get the value I expect, while not getting a compiler warning? The value I expect is essentially an integer listed in include/asm-generic/errno-base.h
of the linux kernel code base.
Since I'm only looking at the pointer as if it was an integer in the case where IS_ERR()
is true, I can be sure that it does in-fact only hold an integer value.
The PTR_ERR()
macro in linux/err.h
, which is where IS_ERR()
is also defined, converts a pointer that's really an error code into the appropriate type (a long
).
You should use something like:
if (IS_ERR(file))
return PTR_ERR(file);
Search for existing uses of PTR_ERR()
in the source and you'll see this is a common pattern.
It might be appropriate for your function to return a long
rather than an int
- but all error codes should be representable in an int
.
You can't properly cast a pointer to a type of smaller size, period. You could do some conversion if you were sure of what that pointer stored.
For example, if you know that a pointer has only lowest 32 bits set you can just cast it and use some compiler-specific pragma to suppress the warning. Or if you want to hash the pointer for using in something like a hash table you could xor the upper 32 bits with the lower 32 bits.
This can't be decided without more knowledge of how that int
is used later.
Im not sure I get how you sometimes want to return an number from errno-base.h and sometimes a pointer -- how would the receiving function be able to tell the two apart? That being equal, then on Linux GCC,
int
is 32bit wide irrespective of whether you are on 32 or 64bit linux- pointers are 64 bit wide on 64 bit architectures, and 32 bite wide on 32 bit architectures
long
are 32bit wide on 32bit architectures and 64 bit wide on 64 bit architectures.long long
are always 64bit wide
hence on a 64bit architecture casting a pointer to an int means that you will case a 64bit value to a 32bit value, and you can be somewhat sure that you will lose part of the 64bit information from the pointer -- and this is what the compiler warning is all about, as you point out yourself.
If you want to cast from pointer to something 'anonymous' then your choices should be either long
, long long
or void*
-- with the void*
being the most portable.
The other alternative is to record it as an offset, that is if you have a large memory area where you want to 'cast' to a 32bit integer, then convert it to something like;
static struct mybigbuffer *globalbuffer;
int cast2int(void*x)
{
return (int)(globalbuffer-(struct mybigbuffer*)x);
}
however that is only work assuming that you know that your your memory will never exceed 2^31 records of globalbuf
and that your pointers are assured to align on boundaries etc -- so unless you are 100% sure you know what you are doing, I would not recommended this either -- stick with the long or void* as the safe options.
精彩评论