naxchange
naxchange

Reputation: 913

C/C++ pointer trick fix

I'm trying this pointer trick and I can't figure out how to fix it, I'm running g++ 4.6 on ubuntu 12.04 64-bit. Check out this code below:

int arr[5];
arr[3] = 50;
((short*) arr)[6] = 2;
cout << arr[3] << endl;

The logic is: since short is 2 bytes, int is 4 bytes, I want to change the first 2 bytes in arr[3], while keeping the value of the second 2 bytes as 50. So I'm just messing with the bit pattern. Unfortunately, sizeof(int*) and sizeof(short*) are both 8 bytes. Is there a type cast that returns a pointer of size 2 bytes?

Update:

I realized that the question is poorly written, so I'll fix that: The output from cout << arr[3] << endl; I'm getting is 2. The output I would like to get is neither 2 nor 50, but rather a large number, indicating that the left part of the int bit pattern has been changed, while the right part (the second 2-bits) of the int stored in arr[3] is still unchanged.

Upvotes: 0

Views: 318

Answers (6)

vonbrand
vonbrand

Reputation: 11831

Said all the above, the standard prohibits doing such things: Setting a variable through a pointer to another type is calling undefined behaviour. If you know how your machine works (sizes of int and short, endianness, ...) and you know how your compiler (is likely to) translate your code, then you might get away with it. Does for neat parlor tricks, and spectacular explosions when the machine/compiler/phase of the moon change.

If it wins any performance, the win will be minimal, and it could even be a net loss (one compiler long ago I fiddled with, playing the "I can implement this loop better than the compiler, I know exactly what goes on here" generated much worse code for my `label: ... if() goto label; than the exact same written natively as a loop: My "smart" code confused the compiler, its "pattern for loops" did not apply).

Upvotes: 0

Billy ONeal
Billy ONeal

Reputation: 106589

sizeof(int*) and sizeof(short*) are both going to be the same -- as will sizeof(void*) -- you're asking for the size of a pointer, not the size of the thing the pointer points to.

Use sizeof(int) or sizeof(short) instead.


Now, as for your code snippet, you are making assumptions about the endianness of the machine on which you're running. The "first" part of the int on a given platform may be the bytes with higher address, or the bytes with lower address.

For instance, your memory block may be laid out like this. Let's say the least significant byte has index zero, and the most significant byte has index one. On a big endian architecture an int may look like this:

 <------------- 4 bytes --------------->
+---------+---------+---------+---------+
| int:3   | int:2   | int:1   | int:0   |
| short:1 | short:0 | short:1 | short:0 |
+---------+---------+---------+---------+

Notice how the first short in the int -- which in your case would have been ((short*) arr)[6] -- contains the most significant bits of the int, not the least significant. So if you overwrite ((short*) arr)[6], you are overwriting the most significant bits of arr[3], which appears to be what you wanted. But x64 is not a big endian machine.

On a little endian architecture, you would see this instead:

 <------------- 4 bytes --------------->
+---------+---------+---------+---------+
| int:0   | int:1   | int:2   | int:3   |
| short:0 | short:1 | short:0 | short:1 |
+---------+---------+---------+---------+

leading to the opposite behavior -- ((short*) arr)[6] would be the least significant bits of arr[3], and ((short*) arr)[7] would be the most significant.


Here's what my machine happens to do -- your machine may be different:

C:\Users\Billy\Desktop>type example.cpp
#include <iostream>

int main()
{
        std::cout << "Size of int is " << sizeof(int) << " and size of short is "
                  << sizeof(short) << std::endl;

        int arr[5];
        arr[3] = 50;
        ((short*) arr)[6] = 2;
        std::cout << arr[3] << std::endl;
        ((short*) arr)[7] = 2;
        std::cout << arr[3] << std::endl;
}


C:\Users\Billy\Desktop>cl /W4 /EHsc /nologo example.cpp && example.exe
example.cpp
Size of int is 4 and size of short is 2
2
131074

Upvotes: 5

shf301
shf301

Reputation: 31404

Your problem is due to endianness. Intel CPU's are little endian meaning that the first byte of an int is stored in the the first address. Let me how you can example:

Let's assume that arr[3] is at address 10:

Then arr[3] = 50; Writes the following to memory

10:   0x32
11:   0x00
12:   0x00
13:   0x00

And ((short*) arr)[6] = 2; writes the following to memory

10:  0x02
11:  0x00

Upvotes: 5

limp_chimp
limp_chimp

Reputation: 15183

You wouldn't want your actual pointer to be of size two bytes; this would mean that it could only access ~16k of memory addresses. However, using a cast, as you are, to a short *, will let you access the memory every two bytes (since the compiler will view the array as an array of shorts, not of ints).

Upvotes: -1

John3136
John3136

Reputation: 29266

You are making a lot of assumptions which might not hold water. Also what does the sizeof pointers have to do with the problem at hand?

Why not just use bit masks:

arr[3] |= (top_2_bytes << 16);

This should set the upper 16 bytes without disturbing the lower 16. (You may get into signed/unsigned dramas)

Upvotes: 0

Tony Delroy
Tony Delroy

Reputation: 106196

When you index a pointer, it adds the index multiplied by the size of the pointed-to type. So, you don't need a 2-byte pointer.

Upvotes: 0

Related Questions