Page 1 of 2
adding pointers portably (in C)
Posted: Sun Dec 31, 2006 11:51 pm
by earlz
well I have been working on a few things and I can't do this portably..
this is my code that doesn't compile:
Code: Select all
volatile void *core; //just so you can see what everything is..
void blah(){
unsigned char *ptr=0x20000; //just for example..
core=malloc(0x100000);
ptr=ptr+core; //add our core address to it
}
that was all done for example sake, but ptr=ptr+core is what gives the error, now I know I could do something like ptr=(unsigned int)ptr+(unsigned int)core; but this won't work when I port it to 64bit so is there a way to do this portably?
Posted: Mon Jan 01, 2007 4:02 am
by Ready4Dis
Is your compiler setup for 64-bit? An int is defined as the default CPU data type (aka, size of a pointer). So,if you compile it casting to an int,it would work if your compiler is in 64-bit mode! That's why I recommend using long's anywhere that you want a 32-bit integer, because int isn't gaurranteed to be one. If you don't trust the compiler (nobody should), what I did was setup a type's header and defined all my own types, as well as one for default pointer size,so when i compile this for any platform, i can simply fill in the appropriate types and i know it will work properly!
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned long u32;
typedef signed long s32;
typedef unsigned long long u64;
typedef signed long long s64;
typedef unsigned long default_type;
For a 64-bit compile, I would simply replace the last typedef with long long and it'd be 64-bit default_type now, and i would use this for any arithmetic for pointers!
Posted: Mon Jan 01, 2007 4:07 am
by earlz
according to some doc I read by microsoft about how to keep 32bit apps portable to 64 bit...
it said that char is 8bit(of course) short is 16bit, int is 32bit(to avoid wasting space) long is 64bit, didn't say anything on long long
Posted: Mon Jan 01, 2007 5:02 am
by Ready4Dis
hckr83 wrote:according to some doc I read by microsoft about how to keep 32bit apps portable to 64 bit...
it said that char is 8bit(of course) short is 16bit, int is 32bit(to avoid wasting space) long is 64bit, didn't say anything on long long
long is not 64-bit, long is 32-bit on most compilers, check it out...
sizeof(long), easy enough
.
sizeof(long long);
sizeof(int)
sizeof(char)
sizeof(short)
This gives you all the answers for your compiler (in bytes, so multiply by 8 to get bits!). The specifications aren't very clear on all the sizes, some they just say that it must be some size greater than a predetemined size, meaning if one wanted, they could write a compiler where an int was 256-bit's... and it would still be within standards. That's why I define my own types based on the compiler I am using, I know for example, gcc uses 32-bit ints, and 32-bit longs, while turbo c uses 16-bit ints and 32-bit longs. turbo is a real mode (so, 16-bit pointers) compiler, so it uses 16-bit integers. A 64-bit compiler *should* use 64-bit int's, however I feel better defining my own types and then not worrying about compiler issues!
Posted: Mon Jan 01, 2007 5:10 am
by Ready4Dis
Just wanted to show you a quick link, notice the long long data type, and the size of a regular long, etc. Keep in mind, these are specific to the microsoft compiler (vc++), but the basic types are identical on gcc x86-32 as far as I can see
. Ok, just read a quick thing on the gcc compiler in 64-bit mode, lol, they use 32-bit ints and 64-bit longs.
http://msdn2.microsoft.com/en-us/librar ... S.80).aspx
Here is the actual standard defining the types:
http://msdn2.microsoft.com/en-us/librar ... S.80).aspx
Notice it says int must be greater than or equal to short, and must be smaller than or equal to a long. This explains why gcc 64 used a 32-bit int. Notice there are no hard definitions or predefined sizes, a char could be 16-bits, and a short,int,long could all be 64-bits, and a long long could be 512-bits, it would still fit the 'standards'. I bet it would break tons of pre-existing code though. Like I said a bunch of times already, define your own sizes, or use the pre-existing ones (someone else pointed out that there are types that have their sizes in them, but they don't define a default size for arithmetic types that deal with pointers, so you'd have to add that one).
Re: adding pointers portably (in C)
Posted: Mon Jan 01, 2007 6:06 am
by Solar
hckr83 wrote:that was all done for example sake, but ptr=ptr+core is what gives the error, now I know I could do something like ptr=(unsigned int)ptr+(unsigned int)core; but this won't work when I port it to 64bit so is there a way to do this portably?
Problems with "adding pointers" usually arise when people don't work type-correct. Figuring out which temporary casts "work" doesn't help much then. (With "portable", you probably mean
portable, not "works with Win32 and Win64"...)
In your example: What does "core" point to? If it is a char array (like ptr), why isn't it a char *, with the return value of malloc() cast to char * immediately?
If it is
not a char array, what are you trying to achieve by adding two pointers that are
semantically incompatible? Of course you can tweak the
syntax to accept this, but what is the
semantic sense behind it - and couldn't you solve the problem
without having to do calculations on incompatible pointer types?
Posted: Mon Jan 01, 2007 7:50 am
by Ready4Dis
There are many reasons to add address' to each other, for example, a dynmic linked library, you would have to run through it's relocation table and add the base offset to each pointer (of course, this type of file would have the operand size in there, but similar cases exist). Another way you could do it is like this:
void *AddPointers(void* ptr1, void *ptr2)
{
unsigned char *c_ptr1, *c_ptr2;
c_ptr1 = (char*)ptr1;
c_ptr2 = (char*)ptr2;
return (void*)(c_ptr1+cptr2);
}
This would convert them both into compatible pointer types, then add them together. You could also make this a macro so you can do other operations besides adding
#define PointerOp(p1,op,ptr2) (void*)((char*)(p1) op (char*)(p2))
So, now you could do something like...
ret = PointerOp(ptr1,+,ptr2);
This isn't very pretty looking though, but hey
.
Posted: Fri Jan 12, 2007 8:14 pm
by earlz
What I use this for is for my emulator..
core points to allocated memory which is for the emulators ram
basically this is only an example thing to access a byte of memory
ret = PointerOp(ptr1,+,ptr2);
that's just too ugly for me...I want for my code to be at least half readable for people that want to look at my code...
void *AddPointers(void* ptr1, void *ptr2)
{
unsigned char *c_ptr1, *c_ptr2;
c_ptr1 = (char*)ptr1;
c_ptr2 = (char*)ptr2;
return (void*)(c_ptr1+cptr2);
}
that puts too much overhead..even if I did it inline then it still has to create a stack frame and add 8 to ebp....which though is only a few clock cycles..if I did that for every pointer it would add up to millions of extra clock cycles that are wasted in a normal emulated program...
Posted: Sat Jan 13, 2007 3:41 am
by Candy
hckr83 wrote:What I use this for is for my emulator..
core points to allocated memory which is for the emulators ram
basically this is only an example thing to access a byte of memory
ret = PointerOp(ptr1,+,ptr2);
that's just too ugly for me...I want for my code to be at least half readable for people that want to look at my code...
void *AddPointers(void* ptr1, void *ptr2)
{
unsigned char *c_ptr1, *c_ptr2;
c_ptr1 = (char*)ptr1;
c_ptr2 = (char*)ptr2;
return (void*)(c_ptr1+cptr2);
}
that puts too much overhead..even if I did it inline then it still has to create a stack frame and add 8 to ebp....which though is only a few clock cycles..if I did that for every pointer it would add up to millions of extra clock cycles that are wasted in a normal emulated program...
Code: Select all
template <class T>
class safe_ptr {
public:
inline safe_ptr<T> operator+(const safe_ptr<T> &other) {
return safe_ptr<T>(_ptr + other._ptr);
}
inline operator safe_ptr<U>() {
return safe_ptr<U>(_ptr);
}
private:
T _ptr;
};
safe_ptr<char *> lpchar1, lpchar2((char *)0xB8000);
lpchar1 = lpchar2 + (char*)0x240;
Inline functions don't get a stack frame. The whole point why c++ isn't slow is that you can inline nearly all empty functions so you can add a big load of language sugar without delaying the execution of the program. The compilation will take longer though, but that should be offset by the improvement in legibility.
The above safe_ptr isn't safe in any way and is very incomplete. You do have hooks you can hang something else into for checking.
Posted: Sat Jan 13, 2007 7:52 am
by Ready4Dis
"adding pointers portably (in C)"
Also, if you don't like my method using a function, you could easily convert it to a macro or similar....
#define AddPointers(p1,p2) ((char*)(p1) + (char*)(p2)))
This would require no stack, no extra overhead and is portable C code.
The C++ code offered was ok, but if you were using C++, I would overload other methods as well, so you can remove the ugly (char*) casting
. Also, that is only good for one data type (unless you do a lot of overloading, not sure if he will want to add an unsigned short* to an unsigned char*, but hey, you never know).
Posted: Sat Jan 13, 2007 8:33 am
by Candy
hckr83 wrote:according to some doc I read by microsoft about how to keep 32bit apps portable to 64 bit...
it said that char is 8bit(of course) short is 16bit, int is 32bit(to avoid wasting space) long is 64bit, didn't say anything on long long
Ready4Dis wrote:Just wanted to show you a quick link, notice the long long data type, and the size of a regular long, etc. Keep in mind, these are specific to the microsoft compiler (vc++), but the basic types are identical on gcc x86-32 as far as I can see
. Ok, just read a quick thing on the gcc compiler in 64-bit mode, lol, they use 32-bit ints and 64-bit longs.
http://msdn2.microsoft.com/en-us/librar ... S.80).aspx
Here is the actual standard defining the types:
http://msdn2.microsoft.com/en-us/librar ... S.80).aspx
Notice it says int must be greater than or equal to short, and must be smaller than or equal to a long. This explains why gcc 64 used a 32-bit int. Notice there are no hard definitions or predefined sizes, a char could be 16-bits, and a short,int,long could all be 64-bits, and a long long could be 512-bits, it would still fit the 'standards'. I bet it would break tons of pre-existing code though. Like I said a bunch of times already, define your own sizes, or use the pre-existing ones (someone else pointed out that there are types that have their sizes in them, but they don't define a default size for arithmetic types that deal with pointers, so you'd have to add that one).
Guys - Microsoft did not define nor invent C++! Nor do they hold any authority over it whatsoever.
Try ISO 9899 or ISO 14882.
For your information:
Char must hold at least 256 different values, short must hold at least 65536 different values, int must be larger than or equal to short, long must be larger than or equal to int and long long must be larger than or equal to long. Note that long long is only defined in C99, so in C89 or in c++ there is no long long (which should explain the warnings/errors you get when turning on warnings for c++ code).
So, all in all, you can't assume you can add two shorts and fit it in a long long.
Posted: Sat Jan 13, 2007 9:19 am
by Ready4Dis
Candy wrote:
Guys - Microsoft did not define nor invent C++! Nor do they hold any authority over it whatsoever.
Try ISO 9899 or ISO 14882.
For your information:
Char must hold at least 256 different values, short must hold at least 65536 different values, int must be larger than or equal to short, long must be larger than or equal to int and long long must be larger than or equal to long. Note that long long is only defined in C99, so in C89 or in c++ there is no long long (which should explain the warnings/errors you get when turning on warnings for c++ code).
So, all in all, you can't assume you can add two shorts and fit it in a long long.
Sorry if my post was misleading, I know that is the MS interpretation of the standard, and not a link to the standard itself. But, it states the exact same info you have stated...
"Type short int (or simply short) is an integral type that is larger than or equal to the size of type char, and shorter than or equal to the size of type int."
"Type int is an integral type that is larger than or equal to the size of type short int, and shorter than or equal to the size of type long."
"Type long (or long int) is an integral type that is larger than or equal to the size of type int."
"Type long long Larger than an unsigned long."
This is consistant with the standard, even if it is a m$ document. The other parts of the document are specific to their implementation (at the bottom where it says sizes of fundamental types, those are specific to msvc), and can be different under other compilers and still fit the above descriptions. Notice on the page however, it actually states Microsoft Specific before that section, and End Microsoft Specific at the bottom, so I figured it'd be an easy to read link for people. We do agree that you cannot assume sizes based on 'standard' data types, which is why I define my own, and use the # of bits in the name, so anybody reading will always know it's size and type. s8 = signed 8-bit, u8 = unsigned 8bit, sX = signed X bits, uX = unsigned X bits. Anybody reading my code will easily know what the size of all data types being passed around are.
Posted: Sat Jan 13, 2007 10:02 am
by Candy
Ready4Dis wrote:Sorry if my post was misleading, I know that is the MS interpretation of the standard, and not a link to the standard itself. But, it states the exact same info you have stated...
You should not quote Microsoft as being authoritative. It's wrong by the way:
"Type short int (or simply short) is an integral type that is larger than or equal to the size of type char, and shorter than or equal to the size of type int."
IIRC, char can be defined larger than short. Not sure on this one. It also DOES specify a minimum size for a short, which Microsoft doesn't.
"Type long long Larger than an unsigned long."
I'm pretty sure even Microsofts x86_64 compiler doesn't do this. It's very common to have long long always 64-bit and the long type as whatever the native type of the processor is. They would both be 64-bit then, which this line forbids.
This is consistant with the standard, even if it is a m$ document. The other parts of the document are specific to their implementation (at the bottom where it says sizes of fundamental types, those are specific to msvc), and can be different under other compilers and still fit the above descriptions. Notice on the page however, it actually states Microsoft Specific before that section, and End Microsoft Specific at the bottom, so I figured it'd be an easy to read link for people. We do agree that you cannot assume sizes based on 'standard' data types, which is why I define my own, and use the # of bits in the name, so anybody reading will always know it's size and type. s8 = signed 8-bit, u8 = unsigned 8bit, sX = signed X bits, uX = unsigned X bits. Anybody reading my code will easily know what the size of all data types being passed around are.
It's different. If you bother to read the actual C iso standard, you'll notice the standard uint*_t and int*_t types that are actually portable. Microsoft doesn't use them either.
Posted: Sat Jan 13, 2007 10:19 am
by Brynet-Inc
As Candy stated
Microsoft != Standard (
In any language.. They always manage to break standards)
Posted: Sat Jan 13, 2007 11:12 am
by Ready4Dis
Whatever, it was an easy reference, and had the information necessary to prove my point, that you cannot rely on char/short/int/long/long long to be of any size, in which we agree. If you think it's easier to refer him to the ISO standard, then he can shell out the money to buy it and find out that what I was saying was correct, that the sizes are to loosely defined to rely on.
IIRC, char can be defined larger than short. Not sure on this one. It also DOES specify a minimum size for a short, which Microsoft doesn't.
That is correct btw, you could have a 32-bit char, and a 16-bit short, but you still agreed on my statement about not using them, so the point was proved. If I was writing a compiler, I would not go on msdn and start going by their standards, but rather than link a .pfd file that costs money, it's easier to reference an article that proves a point. I agree that microsoft is not an authoritive figure on standards, please don't get the wrong idea.