Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » build in data type bigger than pointer size?

This thread is locked; no one can reply to it. rss feed Print
build in data type bigger than pointer size?
Martin Kalbfuß
Member #9,131
October 2007
avatar

Hey,

Maybe someone knows if there's a build-in type (C99) which is guaranteed to be bigger than the size of a pointer.

I would like to store either a pointer or a number in it. The range of numbers isn't important. The only restriction is, that it shouldn't overlap with the range of addresses a pointer can have.

The function taking this kind of parameter should be able to distinguish between those.

A type twice as big as intptr_t would be fine. This way I could simply use the negative numbers.

Thanks,
Martin

http://remote-lisp.spdns.de -- my server side lisp interpreter
http://www.nongnu.org/gm2/ -- Modula-2 alias Pascal++

Audric
Member #907
January 2001

Does it really have to be a native type ? Otherwise I'd use something like:

typedef struct {
  char type; // 0=number 1=address
  union {
    void * address;
    int number;
  };
} T_FOO;

Martin Kalbfuß
Member #9,131
October 2007
avatar

This way you have to pass in the type, too. I would like to have this working for pointers which are plain numbers and don't have type field. A cast or conversion function isn't a problem. I could hide it behind a macro.

Like this:

void my_func(magic_type_t val) {
  if is_pointer(val) {
    ...
  } else if is_other(val) {
    ...
  }
}

void my_func((magic_type_t)"bla");
void my_func((magic_type_t)-2);

http://remote-lisp.spdns.de -- my server side lisp interpreter
http://www.nongnu.org/gm2/ -- Modula-2 alias Pascal++

Matthew Leverton
Supreme Loser
January 1999
avatar

#SelectExpand
1#include <stdio.h> 2#include <stdint.h> 3 4#define SET_MAGIC_NUM(x) (((x) << 1) | 1) 5#define GET_MAGIC_NUM(x) ((x) >> 1) 6#define IS_MAGIC_NUM(x) ((x) & 1) 7 8int main() 9{ 10 int x = 42; 11 intptr_t foo = &x; 12 13 intptr_t bar = SET_MAGIC_NUM(42); 14 15 printf("%d %d %d\n", IS_MAGIC_NUM(foo), IS_MAGIC_NUM(bar), GET_MAGIC_NUM(bar)); 16 17 return 0; 18}

You'll lose one bit for the integers.

Thomas Fjellstrom
Member #476
June 2000
avatar

C99 pseudo code:

struct magic_t {
   char type;
   uint32_t i;
   intptr_t p;
};

magic_t mk_ptr(intptr_t p) { return (magic_t){ 0, 0, p }; }
magic_t mk_int(uint32_t i) { return (magic_t){ 1, i, NULL }; }

void my_func(magic_t m)
{
   if(m.type == 0) ...
   else if(m.type == 1) ...
}

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Audric
Member #907
January 2001

ML: If a pointer is odd (last bit = 1), it will be mistaken as a number.

Martin Kalbfuß
Member #9,131
October 2007
avatar

ML's solution is what I'm looking for. But as Audric said. Isn't the least significant bit of a pointer of importance?

http://remote-lisp.spdns.de -- my server side lisp interpreter
http://www.nongnu.org/gm2/ -- Modula-2 alias Pascal++

Matthew Leverton
Supreme Loser
January 1999
avatar

It depends on what you are doing. Anything that points to the beginning of a "variable" will have a zero low bit. If it's pointing inside a string, then it wouldn't work.

Oscar Giner
Member #2,207
April 2002
avatar

That's completely untrue:

#include <stdio.h>

int main()
{
  char a;
  char b;
  
  printf ("%p, %p\n", &a, &b);
}

One of those pointers will have 1 as low bit.

Thomas Fjellstrom
Member #476
June 2000
avatar

Yeah, most variables, structs and whatnot are aligned to a word boundary. so theoretically you could use the last 2-3 bits to store info if you wanted, and were careful.

Actually the linux kernel is known to use that trick in some locations.

One of those pointers will have 1 as low bit.

Try compiling with optimizations. GCC is supposed to align variables.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Oscar Giner
Member #2,207
April 2002
avatar

MSVC10 with optimizations:

003AFDDE, 003AFDDF

You don't need to align 1 byte variables to word boundaries for anything.

Audric
Member #907
January 2001

ML: It's true that malloc() alway returns aligned addresses (4-byte and 8-byte respectively on 32bit and 64bit systems), but if you look at an array of chars, or individual char fields in a struct (ex: RGB), or local char variables in a function, they don't need to be aligned - even on platforms that are very touchy about alignment, like ARM.

Thomas Fjellstrom
Member #476
June 2000
avatar

You don't need to align 1 byte variables to word boundaries for anything.

Some architectures don't allow unaligned accesses. And while X86 does allow it, its a lot slower than aligned accesses. Now stick those variables in a struct and give it a try.

Audric said:

but if you look at an array of chars, or individual char fields in a struct (ex: RGB), or local char variables in a function, they don't need to be aligned - even on platforms that are very touchy about alignment, like ARM.

Except now you get stuck with a lot of RMW cycles to change the unaligned variables.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

I mentioned there were exceptions. The original question is a hack, and so often you can work within restrictions.

char __attribute__ ((aligned (2)))  a;
char __attribute__ ((aligned (2)))  b;

Now it's aligned. Problem solved.

Oscar Giner
Member #2,207
April 2002
avatar

Some architectures don't allow unaligned accesses. And while X86 does allow it, its a lot slower than aligned accesses. Now stick those variables in a struct and give it a try.

Inside a struct it should be the same, but the compiler will add a padding to the end so the full struct is 4 (or 8) byte aligned (although if the struct only contains chars, this should not be necessary, so a good optimizer won't add any padding in this case).

ARM can perfectly access a single byte residing anywhere inside a word, an x86 doesn't suffer from any performance hit from this. The problem comes with data > 1 byte. Your definition of when a variable is aligned or not is wrong. Look at the chart at the end of this page, for example: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/aligned.html

Arthur Kalliokoski
Second in Command
February 2005
avatar

If you were to "align" byte data, then the next byte to access is unaligned by definition, so what price alignment?

They all watch too much MSNBC... they get ideas.

Matthew Leverton
Supreme Loser
January 1999
avatar

If you were to "align" byte data, then the next byte to access is unaligned by definition, so what price alignment?

What is the question?

If Martin simply wants to distinguish between object pointers and integer values, my solution will work unless somebody is intentionally trying to break it.

If he needs to accept pointers to chars, then it requires more careful alignment. And some cases simply wouldn't be possible to support (i.e., subsets of C-strings). If those are common cases that he needs to support, then I'd go with something like Audric has suggested.

Oscar Giner
Member #2,207
April 2002
avatar

Who says a pointer to an object cannot have an address ending in 1? :P

#SelectExpand
1#include <cstdio> 2#include <vector> 3 4class test 5{ 6 char a; 7 char b; 8 char c; 9}; 10 11int main() 12{ 13 test test_array[10]; 14 std::vector<test> test_vector; 15 test_vector.push_back(test()); 16 test_vector.push_back(test()); 17 test_vector.push_back(test()); 18 test_vector.push_back(test()); 19 20 test *pointer_to_object; 21 22 pointer_to_object = &(test_array[1]); 23 printf ("pointer_to_object = %p\n", pointer_to_object); 24 25 pointer_to_object = &(test_vector[1]); 26 printf ("pointer_to_object = %p\n", pointer_to_object); 27}

Output:

pointer_to_object = 0020F9EF
pointer_to_object = 007F2FD3

Again, compiled with MSVC10, with optimizations enabled.

relpatseht
Member #5,034
September 2004
avatar

Just use the highest bit then if you're worried about the lowest. You'll never get an address that high--that is operating system territory.

On x86 for at least Windows, but I think linux as well, every application is only given the lower 2gb of virtual address space. On x64, it is typically the lower 48 bits (off-hand, needs verification). You can use the highest bit to store some information if you want to.

Matthew Leverton
Supreme Loser
January 1999
avatar

I don't know why this bothers you so much. Perhaps you are just being anal over my choice of words.

You're contriving a case with an unaligned object using an odd amount of single byte data structs. Throw an int in the struct, and it will magically become aligned. As I've said from the start, this isn't a general purpose solution. However, it can be a valid solution depending on what is trying to be accomplished.

You can use the highest bit to store some information if you want to.

But what if I am writing an operating system? :o

relpatseht
Member #5,034
September 2004
avatar

#define SET_MAGIC_NUM(x) ((x) | (1 << (sizeof(intptr_t)*8 - 1)))
#define GET_MAGIC_NUM(x) ((x) & (1 << ((sizeof(intptr_t)*8 - 1)-1)))
#define IS_MAGIC_NUM(x)  ((x) & (1 << (sizeof(intptr_t)*8 - 1)))

No alignment problems. Works on all pointers in your program. Of course, it won't work with negative numbers, but neither will ML's solution. If you need negatives, then you really can't use hacks like this at all...

Thomas Fjellstrom
Member #476
June 2000
avatar

Ok, seems I'm wrong, char's aren't aligned on ia-32/amd-64. I thought they were. However, if you intersperse integers into the chars, the ints WILL be aligned to word boundaries.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Peter Wang
Member #23
April 2000

A lot of programming language implementations use the trick Matthew described (tag bits).

verthex
Member #11,340
September 2009
avatar

Audric said:

Does it really have to be a native type ? Otherwise I'd use something like:

typedef struct {
char type; // 0=number 1=address
union {
void * address;
int number;
};
} T_FOO;

Question:

When you have a type like,

void* address

is that only for addresses or is there another use for them?

LennyLen
Member #5,313
December 2004
avatar

verthex said:

When you have a type like,
void* address

is that only for addresses or is there another use for them?

http://stackoverflow.com/questions/1025579/when-to-use-a-void-pointer

Go to: