Sign in to follow this  
Thevenin

Annoyances with pointer offsets.

Recommended Posts

Thevenin    270
As part of my DIB implementation, I'm reminded of this little annoyance.
int luiMain[2];

luiMain[0] = 10;
luiMain[1] = 10;

*(&luiMain[0] + 1) == luiMain[1]

In order to add 1 byte, instead of 4 bytes, you gotta cast it...
#include <stdio.h>
void main()
{
	int *laiMain;
	int luiX;
	laiMain = malloc(4*5);
	laiMain[0] = 1;
	laiMain[1] = 5;
	laiMain[2] = 10;
	laiMain[3] = 15;
	laiMain[4] = 20;
	
	for(luiX=0;luiX<=8;luiX++) printf("laiMain[%d]=%d\n", luiX, *(((char *)&laiMain[0]) + luiX));
}

Anyone know a way to disable this? lol, why did they decide on this?! It really doesn't help at all! [Edited by - Thevenin on July 4, 2005 8:41:48 PM]

Share this post


Link to post
Share on other sites
Verg    450
Looks like you're trying to inspect individual bytes within each int.

Do you use/understand hex? If so... why not use this instead?


int luiX;
laiMain[] = { 1, 5, 10, 15, 20 };

for(luiX = 0; luiX < sizeof(laiMain); luiX++)
printf("laiMain[%d]=%.8X\n", luiX, &laiMain[luiX]);


Seems like that would be the simplest way to inspect bytes (in groups of four)

I'm at a loss as to why you need to iterate through the array of ints one byte at a time, though [smile]

Share this post


Link to post
Share on other sites
Thevenin    270
The need arose because I went and did this...

void *Surface = [int][int][struct loRGB][struct loRGB][struct loRGB]...

The first two ints are the width and height, and the rest (Roughly 250k) are all RGB structs.

Inaddition, instead of calling it a void, I went and called it...

struct tRGB *Surface = [int][int][struct loRGB][struct loRGB][struct loRGB]...

So now its impossible to access the first two ints properly without breaking off of the "Multiple of 3" rule imposed by the automated structure cast.

I work with Hex quite often when cheating on DungeonCrawl, but in the programming environment I've never figured out what 0x means, is that just a symbol representing that the following is in Hex?

Share this post


Link to post
Share on other sites
Anon Mike    1098
They did it that way because if you have a pointer to something it makes no sense for p+1 to point into the middle of it. It also falls out naturally from the syntax for arrays.

Share this post


Link to post
Share on other sites
Thevenin    270
Quote:
Original post by Anon Mike
They did it that way because if you have a pointer to something it makes no sense for p+1 to point into the middle of it. It also falls out naturally from the syntax for arrays.


But, but but but.. but

Array[x+1] == I'm ok with that +1 shifting it the sizeof(element).
Array[x] + 1 == This is ludakris, it should shift it 1 byte, not sizeof(element) [headshake]

Share this post


Link to post
Share on other sites
Quote:
Original post by Thevenin
Array[x+1] == I'm ok with that +1 shifting it the sizeof(element).
Array[x] + 1 == This is ludakris, it should shift it 1 byte, not sizeof(element) [headshake]


Those are quite diffrent, one references the element at element (x + 1) the other is the element at x and has one added to it. But let's pretend you ment
&Array[x] + 1
then that wouldn't reference an element but is equal to Array + x + 1, and I guess understanding that is the source of the your problem. Array[x] is just convinent shorthand for *(Array + x), actually it extends so far as to actually being able to write x[Array] (disregarding the fact that anyone who actually use that construct should be shot.

Share this post


Link to post
Share on other sites
iMalc    2466
Quote:
Original post by Thevenin
The need arose because I went and did this...

void *Surface = [int][int][struct loRGB][struct loRGB][struct loRGB]...

The first two ints are the width and height, and the rest (Roughly 250k) are all RGB structs.

Inaddition, instead of calling it a void, I went and called it...

struct tRGB *Surface = [int][int][struct loRGB][struct loRGB][struct loRGB]...

So now its impossible to access the first two ints properly without breaking off of the "Multiple of 3" rule imposed by the automated structure cast.

I work with Hex quite often when cheating on DungeonCrawl, but in the programming environment I've never figured out what 0x means, is that just a symbol representing that the following is in Hex?
Can you please show what you mean in terms of real C++ code with regards to tRGB above. You can't possibly mean a 5-dimensional array!

Also, please explain what you mean by a "multiple of 3 rule". Those first two ints are aligned to int boundaries as far as I'm concerned from what you've posted this far.

I do not see a reason for you to attempt to circumvent the automatic pointer offset arithmetic, thus far. If it's behaviour seems wrong, then you are probably doing something in a bad way. Show some real code so we can show you the way that you should really be doing it.

Share this post


Link to post
Share on other sites
Zahlman    1682
KISS. (I assume plain C.)


typedef struct tRGB_s {
int x;
int y;
// The easiest thing is to just add a level of indirection.
// C's unbelievably weak typing also lets you get away with just declaring
// an array[1] here, and then over-allocating memory accordingly to create
// the struct.
struct loRGB* data;
} tRGB;

tRGB* createTRGB(int x, int y) {
tRGB* thing = malloc(sizeof tRGB); if (!thing) return NULL;
thing->x = x; thing->y = y; thing->data = malloc(sizeof (struct loRGB) * x * y);
if (!thing->data) { free(thing); return NULL; }
return thing;
}

int destroyTRGB(tRGB* target) {
if (!target) return -1;
free(target->data); // it is an invariant that this won't be NULL.
free(target);
return 0;
}

tRGB* cloneTRGB(tRGB* src) {
tRGB* result = createTRGB(src->x, src->y); if (!result) return NULL;
for (int i = 0; i < src->x * src->y; ++i) { result->data[i] = src->data[i]; }
return result;
}

int assignTRGB(tRGB* src, tRGB* dest) {
if (!(src && dest)) return -1;
tRGB* other = cloneTRGB(src); if (!other) return -1;
dest->x = other->x; dest->y = other->y;
free(dest->data); dest->data = other->data; free(other);
return 0;
}



In C++ this kind of thing becomes much easier:


struct tRGB {
int x;
int y;
loRGB* data;
tRGB(int x, int y) : x(x), y(y), data(new loRGB[x*y]) {}
tRGB(const tRGB& other) x(other.x), y(other.y), data(new loRGB[x*y]) {
for (int i = 0; i < x*y; ++i) { data[i] = other.data[i]; }
}
tRGB& operator=(const tRGB& rhs) {
// copy and swap idiom, for exception safety. I used something similar in
// the C code.
tRGB other(rhs);
x = other.x; y = other.y; std::swap(data, other.data);
}
~tRGB() { delete[] data; }
};

Share this post


Link to post
Share on other sites
Thevenin    270
I do not mean a 5-dimensional array.

(&array[1] + 1) is EXACTLY equivalent to (&array[2])

I'm fine with this if its bytes (Since +1 byte <=> the next byte), but when working with structures (Or any variable greater than 1 byte in size), I disagree with the standards.

Quote:
Original post by iMalcIf it's behaviour seems wrong, then you are probably doing something in a bad way. Show some real code so we can show you the way that you should really be doing it.


Ok.

You have a very large block of linear memory...
[][][][][][][][][][][][][][][][][]

You store int's in the first two
[int][int][][][][][][][][][][][][]

Than you store struct RGB (A structure of 3 unsigned characters) in the remaining.
[int][int][sturct RGB][sturct RGB][sturct RGB][sturct RGB][sturct RGB]...

And you call this a surface.

Now when you call my sDrawSurface() function, you don't have to specify the width and height of the surface your drawing, because it already knows, because its already in the surface itself (My reason for doing it this way).

Its really just this simple.

The "Multiple of 3" rule is reference to the fact, that I cannot say arrSurface[{Some Decimal Value Here}] and get the height.

Observe...
struct RGB MyRGBArray;

MyRGBArray[0] == 0th byte.
MyRGBArray[1] == 3rd byte.
MyRGBArray[2] == 6th byte.
MyRGBArray[3] == 9th byte.
MyRGBArray[4] == 12th byte.

However, my the width and height are on the 0th byte && 4th byte.

And so I was hoping....

&MyRGBArray[0] + 4 == 4th byte.

However, it actually equals....

&MyRGBArray[0] + 4 == 12th byte.

Which is ludakris!

Share this post


Link to post
Share on other sites
DrEvil    1148
arrays are meant to store 1 data type. array indexing and pointer arithmetic work the same way. use a byte as your pointer type if you want to increment by the size of a byte.

How about instead of trying to hack something into working, you use the right tool for the job, such as a struct?

struct RGB
{
unsigned char r,g,b;
}

struct mysurfacestruct
{
int a;
int b;
RGB color1;
RGB color2;
RGB color3;
}

then you can have an array of these structs if you want.

Share this post


Link to post
Share on other sites
Thevenin    270
You mean...

struct mysurfacestruct
{
int a;
int b;
struct RGB *colors;
}

And than....

mySurface.colors = malloc(width*height);

Since otherwise it would be a struct with indefinent elements.

Share this post


Link to post
Share on other sites
Fruny    1658
Quote:
(&array[1] + 1) is EXACTLY equivalent to (&array[2])


That's because foo[i] really means *(foo + i).

Quote:
I'm fine with this if its bytes (Since +1 byte <=> the next byte), but when working with structures (Or any variable greater than 1 byte in size), I disagree with the standards.


I don't. It's perfectly rational. Get over it. Pointer arithmetic is intended to iterate over arrays - as such the step must be the size of the array element. If you want to apply a different offset, cast to char*.


template<class T>
T* byte_offset(T* ptr, long offset)
{
return (T*)(offset + (char*)ptr);
}

Share this post


Link to post
Share on other sites
Anon Mike    1098
Think of it this way. int *p = foo();

p now points to an int. You can dereference p and do any sort of int related thing you want. If p+1 pointed to the next byte rather than the next element what would p point to? It would point to garbage. Dereferencing p may very well crash your app (because of alignment or other hardware design issues). The fact that in this particular case ((int *)((byte *)p)+1) might point to something useful is an accident caused by circumventing the type system.

Besides, the vast majority of time people work with elements and having to go p+sizeof(*p) all the time would be annoying and very error-prone.

Share this post


Link to post
Share on other sites
Zahlman    1682
Keep in mind that on some systems, unaligned memory accesses aren't possible:


int foo;
*static_cast<int*>(static_cast<char*>(&foo) + 1); // Boom
// I think this happens to be ok on x86 architecture, but will blow up on
// almost everything Motorola ever made :/

Share this post


Link to post
Share on other sites
smart_idiot    1298
x[y] is *(x+y) is *(y+x) is y[x], now and for ever.

As a result you can write something silly like cout << 4["Hello"] to print 'o'.

Even if it did do what you wanted in your example, you still end up with an integer when you dereference it, not the byte you wanted. You'd need to cast it regardless. The current method is better because you can skip multiplying by it's size, which you would need to do in every other case.

Share this post


Link to post
Share on other sites
iMalc    2466
The better way I spoke of would be:
struct RGB {
unsigned char r, g, b;
};

struct MySurface {
int width, height;
RGB colorData[0]; //May have to use a 1 here if your compiler wont allow zero length arrays.
};
Of course, you'd allocate a MySurface with (width*height*3)+8 bytes.
It's laid out exactly the same in memory and you can access everything sensibly.

Do you now see how you were basically using a high-level-language as if it were a low-level-language (like asm). Make sure that your data structure matches the actual data. You were making things hard for yourself by not doing so.

Compiler pointer arithmetic addressing behaviour IS CORRECT, there is no question about that.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this