Bitfield and Union - unexpected result in C - c

I've been given the following assignment in a C-course:
I've implemented the assignment to decode the 8 byte long long int 131809282883593 as follows:
#include <stdio.h>
#include <string.h>
struct Message {
unsigned int hour : 5;
unsigned int minutes : 6;
unsigned int seconds : 6;
unsigned int day : 5;
unsigned int month : 4;
unsigned int year : 12;
unsigned long long int code : 26;
}; // 64 bit in total
union Msgdecode {
long long int datablob;
struct Message elems;
};
int main(void) {
long long int datablob = 131809282883593;
union Msgdecode m;
m.datablob = datablob;
printf("%d:%d:%d %d.%d.%d code:%lu\n", m.elems.hour, m.elems.minutes,
m.elems.seconds, m.elems.day, m.elems.month, m.elems.year,(long unsigned int) m.elems.code);
union Msgdecode m2;
m2.elems.hour = 9;
m2.elems.minutes = 0;
m2.elems.seconds = 0;
m2.elems.day = 30;
m2.elems.month = 5;
m2.elems.year = 2017;
m2.elems.code = 4195376;
printf("m2.datablob: should: 131809282883593 is: %lld\n", m2.datablob); //WHY does m2.datablob != m.datablob?!
printf("m.datablob: should: 131809282883593 is: %lld\n", m.datablob);
printf("%d:%d:%d %d.%d.%d code:%lu\n", m2.elems.hour, m2.elems.minutes,
m2.elems.seconds, m2.elems.day, m2.elems.month, m2.elems.year, (long unsigned int) m2.elems.code);
}
TRY IT ONLINE.
..what gives me a hard time is the output. The decoding/encoding works nicely so far. 9:0:0 30.5.2017 and code 4195376 is expected, but the difference in the 'datablob' really isn't - and i can't figure out why/where it stems from:
9:0:0 30.5.2017 code:4195376
m2.datablob: should: 131809282883593 is: 131810088189961
m.datablob: should: 131809282883593 is: 131809282883593
9:0:0 30.5.2017 code:4195376
As you can see the datablob is close to the original - but not the original. I've consulted a coworker whos fluent in C about this - but we couldn't figure out the reason for this behaviour.
Q: Why do the blobs differ from each other?
Bonus-Q: When manipulating the union Msgdecode to include another field, a strange thing happens:
union Msgdecode {
long long int datablob;
struct Message elems;
char bytes[8]; // added this
};
Outcome:
9:0:0 30.5.2017 code:0
m2.datablob: should: 131809282883593 is: 8662973939721
m.datablob: should: 131809282883593 is: 131809282883593
9:0:0 30.5.2017 code:4195376
PS: reading on SO about bitfields+union questions gave me the impression that they are rather unreliable. Can this be generally said?

The layout of bitfields within a struct and any padding that may exist between them is implementation defined.
From section 6.7.2.1 of the C standard:
11 An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field
that immediately follows another bit-field in a structure shall be
packed into adjacent bits of the same unit. If insufficient space
remains, whether a bit-field that does not fit is put into
the next unit or overlaps adjacent units is
implementation-defined. The order of allocation of bit-fields within
a unit (high-order to low-order or low-order to high-order) is
implementation-defined. The alignment of the addressable storage
unit is unspecified.
This means that you can't rely on the layout in a standard-compliant manner.
That being said, lets take a look at how the bits are being laid out in this particular case. To reiterate, everything from here down is in the realm of implementation defined behavior. We'll start with the second case where m2.datablob is 8662973939721 as that is easier to explain.
First let's look at the bit representation of the values you assign to m2:
- hour: 9: 0 1001 (0x09)
- minutes: 0: 00 0000 (0x00)
- seconds: 0: 00 0000 (0x00)
- day: 30: 11 1110 (0x3E)
- month: 5: 0101 (0x05)
- year: 2017: 0111 1110 0001 (0x7e1)
- code: 4195376: 00 0100 0000 0000 0100 0011 0000 (0x0400430)
Now let's look at the blob values, first m which assigns to blobvalue then m2 which assigns to each field individually with the above values:
131809282883593 0x77E13D7C0009 0111 0111 1110 0001
0011 1101 0111 1100 0000 0000 0000 1001
8662973939721 0x07E1017C0009 0000 0111 1110 0001
0000 0001 0111 1100 0000 0000 0000 1001
If we start by looking at the values from the right going left, we can see the value 9, so there's our first 5 bits. Next is two sets of 6 zero bits for the next two fields. After that, we see the bit patterns for 30, then 5.
A little further up we see the bit pattern for the value 2017, but there are 6 bits set to zero between this value and the prior ones. So it looks like the layout is as follows:
year ??? month day sec min hour
------------ ----- --- ---- ------ ----- -----
| | | || || || || | |
0000 0111 1110 0001 0000 0001 0111 1100 0000 0000 0000 1001
So there's some padding between the year and month fields. Comparing the m and m2 representations, the differences are in the 6 bits of padding between month and year as well as 4 bits to the left of year.
What we don't see here are the bits for the code field. So just how big is the struct?
If we add this to the code:
printf("size = %zu\n", sizeof(struct Message));
We get:
size = 16
It's considerable bigger than we thought. So let's make the bytes array unsigned char [16] and output it. The code:
int i;
printf("m: ");
for (i=0; i<16; i++) {
printf(" %02x", m.bytes[i]);
}
printf("\n");
printf("m2:");
for (i=0; i<16; i++) {
printf(" %02x", m2.bytes[i]);
}
printf("\n");
Output:
m: 09 00 7c 3d e1 77 00 00 00 00 00 00 00 00 00 00
m2: 09 00 7c 01 e1 07 00 00 30 04 40 00 00 00 00 00
Now we see the 0x0400430 bit pattern corresponding to the code field in the representation for m2. There are an additional 20 bits of padding before this field. Also note that the bytes are in the reverse order of the value which tells us we're on a little-endian machine. Given the way the values are laid out, it's also likely that the bits within each byte are also little-endian.
So why the padding? It's most likely related to alignment. The first 5 fields are 8 bits or less, meaning they each fit into a byte. There is no alignment requirement for single bytes, so they are packed. The next field is 12 bits, meaning it needs to fit into a 16 bit (2 byte) field. So 6 bits of padding are added so this field starts on a 2 byte offset. The next field is 26 bits, which needs a 32 bit field. This would mean it needs to start on a 4 byte offset and use 4 bytes, however since this field is declared unsigned long long, which in this case is 8 bytes, the field uses up 8 bytes. Had you declared this field unsigned int it would probably still start on the same offset but only use up 4 more bytes instead of 8.
Now what about the first case where the blob value is 131810088189961? Let's look at its representation compared to the "expected" one:
131809282883593 0x77E13D7C0009 0111 0111 1110 0001
0011 1101 0111 1100 0000 0000 0000 1001
131810088189961 0x77E16D7C0009 0111 0111 1110 0001
0110 1101 0111 1100 0000 0000 0000 1001
These two representations have the same values in the bits that store the data. The difference between them is in the 6 padding bits between the month and year fields. As to why this representation is different, the compiler probably made some optimizations when it realized certain bits weren't or couldn't be read or written. By adding a char array to the union, it because possible that those bits could be read or written so that optimization could no longer be made.
With gcc, you could try using __attribute((packed)) on the struct. Doing that gives the following output (after adjusting the bytes array to 8 along with the loop limits when printing):
size = 8
9:0:0 30.5.2127 code:479
m2.datablob: should: 131809282883593 is: 1153216309106573321
m.datablob: should: 131809282883593 is: 131809282883593
9:0:0 30.5.2017 code:4195376
m: 09 00 7c 3d e1 77 00 00
m2: 09 00 7c 85 1f 0c 01 10
And the bit representation:
1153216309106573321 0x10010C1F857C0009 0001 0000 0000 0001 0000 1100 0001 1111
1000 0101 0111 1100 0000 0000 0000 1001
131810088189961 0x77E16D7C0009 0000 0000 0000 0000 0111 0111 1110 0001
0110 1101 0111 1100 0000 0000 0000 1001
But even with this, you could run into issues.
So to summarize, with bitfields there's no guarantee of the layout. You're better off using bit shifting and masking to get the values in and out of the bitfields rather than attempting to overlay it.

The problem here lies in line 37 where you do:
m2.elems.code = 4195376;
You assigned a invalid type to your bit field:
struct Message {
unsigned int hour : 5;
unsigned int minutes : 6;
unsigned int seconds : 6;
unsigned int day : 5;
unsigned int month : 4;
unsigned int year : 12;
unsigned long long int code : 26; <-- invalid
};
see: https://www.tutorialspoint.com/cprogramming/c_bit_fields.htm
in the Topic: Bit Field Declaration
There it says you can only use int, signed int and unsigned int as Type.
I think the Compiler interpretes m2.elems.code as int and i don't know what he exactly does with an assignment greater than max int.

To reiterate, the layout of bits within bit-field structs is not guaranteed (i.e. it is compiler dependent) and so this kind of bit manipulation is not good practice. To achieve such functionality bit manipulation should be used instead.
A quick example of this might be:
#define HOUR_BIT_START 59 // The bit number where hour bits starts
#define HOUR_BIT_MASK 0x1F // Mask of 5 bits for the hour bits
unsigned int getHour(long long int blob)
{
return (unsigned int)((blob >> HOUR_BIT_START) & HOUR_BIT_MASK);
}
int main (int argc, char *argv[])
{
unsigned long long int datablob = 131809282883593;
printf("%d:%d:%d %d.%d.%d code:%lu\n", getHour(datablob), getMinutes(datablob), getSeconds(datablob), getDay(datablob), getMonth(datablob), getyear(datablob), getCode(datablob));
}
I'll leave the implementation of the other get*() functions to you

Related

Bitshift (rotation) with hexadecimal in C [duplicate]

This question already has answers here:
Best practices for circular shift (rotate) operations in C++
(16 answers)
Closed 2 years ago.
I'm trying to rotate hexadecimal numbers in C. The problem I have is that, with each loop. more zeros occur in the number.
Here is my code:
int main (void) {
int hex = 0x1234ABCD;
for(int i=0; i<12;i++,hex <<=4){
printf("0x%04x %d ",hex,hex );
pattern(hex);
}
return 0;
}
I saw some other code on this site which added & 0x0F to the shift, but it is not working for me.
Here are my results with the compiler
0x1234abcd 305441741 0001 0010 0011 0100 1010 1011 1100 1101
0x234abcd0 592100560 0010 0011 0100 1010 1011 1100 1101 0000
0x34abcd00 883674368 0011 0100 1010 1011 1100 1101 0000 0000
0x4abcd000 1253888000 0100 1010 1011 1100 1101 0000 0000 0000
0xabcd0000 -1412628480 1010 1011 1100 1101 0000 0000 0000 0000
Thank you for your help.
There is no operator that does a rotation for you. You need to combine 2 shift operations. Also you should use unsigned values when doing bitshift operations.
int main (void) {
unsigned int hex = 0x1234ABCD;
for(int i=0; i<12;i++) {
printf("0x%04x %d ",hex,hex );
pattern(hex);
unsigned int upper = hex >> (sizeof(hex)*CHAR_BIT - 4);
hex <<= 4;
hex |= upper & 0x0F;
}
return 0;
}
When you use the left-shift (<<) operator on a number, shifting by n bits, then the most significant n bits of that number are lost, and the least significant n bits are filled with zeros (as you have noticed).
So, in order to perform a bitwise rotation, you need to first 'save' those top 4 bits, then put them back (using the | operator after shifting them down to the bottom).
So, assuming a 32-bit int size, something like this:
#include <stdio.h>
int main(void)
{
int hex = 0x1234ABCD;
for (int i = 0; i < 12; i++) {
printf("0x%04x %d ", hex, hex);
pattern(hex);
int save = (hex >> 28) & 0x0F; // Save TOP four bits of "hex" in BOTTOM 4 of "save"
hex <<= 4; // Now shift the bits to the left ...
hex |= save; // ... then put those saved bits in!
}
return 0;
}
Note: We mask the save value with 0x0F after the shift to make sure that all other bits are 0; if we don't do this then, with negative numbers, we are likely to get those other bits filled with 1s.

using C Pointer with char array

int i=512;
char *c = (char *)&i;
c[0] =1;
printf("%d",i);
this displays "513", it adds 1 to i.
int i=512;
char *c = (char *)&i;
c[1] =1;
printf("%d",i);
whereas this displays 256. Divides it by 2.
Can someone please explain why? thanks a lot
Binary
The 32-bit number 512 expressed in binary, is just:
00000000000000000000001000000000
because 2 to the power of 9 is 512. Conventionally, you read the bits from right-to-left.
Here are some other decimal numbers in binary:
0001 = 1
0010 = 2
0011 = 3
0100 = 4
The Cast: Reinterpreting the Int as an Array of Bytes
When you do this:
int i = 512;
char *c = (char *)&i;
you are interpreting the 4-byte integer as an array of characters (8-bit bytes), as you probably know. If not, here's what's going on:
&i
takes the address of the variable i.
(char *)&i
reinterprets it (or casts it) to a pointer to char type. This means it can now be used like an array. Since you know an int is at least 32-bit on your machine, can access its bytes using c[0], c[1], c[2], c[3].
Depending on the endianness of the system, the bytes of the number might be laid out: most significant byte first (big endian), or least significant byte first (little endian). x86 processors are little endian. This basically means the number 512 is laid out as in the example above, i.e.:
00000000 00000000 00000010 00000000
c[3] c[2] c[1] c[0]
I've grouped the bits into separate 8-bit chunks (bytes) corresponding to the way they are laid out in memory. Note, you also read them right-to-left here, so we can keep with conventions for the binary number system.
Consequences
Now setting c[0] = 1 has this effect:
00000000 00000000 00000010 00000001
c[3] c[2] c[1] c[0]
which is 2^9 + 2^0 == 513 in decimal.
Setting c[1] = 1 has this effect:
00000000 00000000 00000001 00000000
c[3] c[2] c[1] c[0]
which is 2^8 == 256 in decimal, because you've overwritten the second byte 00000010 with 00000001
Do note on a big endian system, the bytes would be stored in reverse order to a little endian system. This would mean you'd get totally different results to ones you got if you ran it on one of those machines.
Remember char is 8 bit, 512 is bit representation is
512 = 10 0000 0000
when you do char *c = (char *)&i; you make:
c[1] = 10
c[0] = 0000 0000
when you do c[0] = 1
you make it 10 0000 0001 which is 513.
when you do c[1] = 1, you make it 01 0000 0000 which is 256.
Before you wonder why what you're seeing is "odd", consider the platform you're running your code on, and the endianness therein.
Then consider the following
int main(int argc, char *argv[])
{
int i=512;
printf("%d : ", i);
unsigned char *p = (unsigned char*)&i;
for (size_t j=0;j<sizeof(i);j++)
printf("%02X", p[j]);
printf("\n");
char *c = (char *)&i;
c[0] =1;
printf("%d : ", i);
for (size_t j=0;j<sizeof(i);j++)
printf("%02X", p[j]);
printf("\n");
i = 512;
c[1] =1;
printf("%d : ", i);
for (size_t j=0;j<sizeof(i);j++)
printf("%02X", p[j]);
printf("\n");
return 0;
}
On my platform (Macbook Air, OS X 10.8, Intel x64 Arch)
512 : 00020000
513 : 01020000
256 : 00010000
Couple what you see above with what you have hopefully read about endianness, and you can clearly see my platform is little endian. So whats yours?
Since you are aliasing an int through a char pointer, and a char is 8 bits wide (a byte), the assignment:
c[1] = 1;
will set the second byte of i to 000000001. Bytes 1, 3 and 4 (if sizeof(int) == 4) will stay unmodified. Previously, that second byte was 000000010 (since I assume you're on an x86-based computer, which is a little-endian architecture.) So basically, you shifted the only bit that was set one position to the right. That's a division by 2.
On a little-endian machine and a compiler with 32-bit int, you originally had these four bytes in i:
c[0] c[1] c[2] c[3]
00000000 00000010 00000000 00000000
After the assignment, i was set to:
c[0] c[1] c[2] c[3]
00000000 00000001 00000000 00000000
and therefore it went from 512 to 256.
Now you should understand why c[0] = 1 results in 513 :-) Think about which byte is set to 1 and that the assignment doesn't change the other bytes at all.
It's because your machine is little endian, meaning the least-significant byte is stored first in memory.
You said int i=512;. 512 is 0x00000200 in hex (assuming a 32-bit OS for simplicity). Let's look at how i would be stored in memory as hexadecimal bytes:
00 02 00 00 // 4 bytes, least-significant byte first
Now we interpret that same memory location as a character array by doing char *c = (char *)&i; - same memory, different interpretation:
00 02 00 00
c[0][1][2][3]
Now we change c[0] with c[0] =1; and the memory looks like
01 02 00 00
Which means if we look at it as a little endian int again (by doing printf("%d",i);), it's hex 0x00000201, which is 513 decimal.
Now if we go back and change c[1] with c[1] =1;, your memory now becomes:
00 01 00 00
Now we go back and interpret it as a little endian int, it's hex 0x00000100, which is 256 decimal.
It's depends on the machine whether that is little endian or big endian that how data is stored in bits.for more read this about endianness
C language doesn't guarantee about this .
512 in binary :
=============================================
0000 0000 | 0000 0000 | 0000 0010 | 0000 0000 ==>512
=============================================
12 34 56 78
(0x12345678 suppose address of this int)
char *c =(char *)&i now c[0] either point to 0x78 or 0x12
Modifying the value using c[0] may result to 513 if it points to 0x78
=============================================
0000 0000 | 0000 0000 | 0000 0010 | 0000 0001 ==> 513
=============================================
or, can be
=============================================
0000 0001 | 0000 0000 | 0000 0010 | 0000 0000 ==>2^24+512
=============================================
Similarly for 256 also : because your c1 will have the address of 2nd byte from right.
in figure below,
=============================================
0000 0000 | 0000 0000 | 0000 0001 | 0000 0000 ==>256
=============================================
So its implemention of representation of numbers in our system

48 byte binary to 6 byte binary

I read a 17 byte hex string from command line "13:22:45:33:99:cd" and want to convert it into binary value of length 6 bytes.
For this, I read a 17 byte hex string from command line "13:22:45:33:99:cd" and converted it into binary digits as 0001 0011 0010 0010 0100 0101 0011 0011 1001 1001 1100 1101. However, since I am using a char array, it is of length 48 bytes. I want the resulting binary to be of length 6 bytes i.e, I want 0001 0011 0010 0010 0100 0101 0011 0011 1001 1001 1100 1101 to be of 6 bytes (48 bits) and not 48 bytes. Please suggest how to accomplish this.
Split the string on the ':' character, and each substring is now a string you can pass to e.g. strtoul and then put in a unsigned char array of size 6.
#include <stdio.h>
const char *input = "13:22:45:33:99:cd";
int output[6];
unsigned char result[6];
if (6 == sscanf(input, "%02x:%02x:%02x:%02x:%02x:%02x",
&output[0], &output[1], &output[2],
&output[3], &output[4], &output[5])) {
for (int i = 0; i < 6; i++)
result[i] = output[i];
} else {
whine("something's wrong with the input!");
}
You pack them together. Like high nibble with low nibble: (hi<<4)|lo (assuming both are 4-bits).
Although you may prefer to convert them byte by byte, not digit by digit in the first place.

C printing char array as float

I'm trying to print a char array of 4 elements as a float number. The compiler(gcc) won't allow me to write z.s={'3','4','j','k'}; in the main() function, why?
#include <stdio.h>
union n{
char s[4];
float x;
};
typedef union n N;
int main(void)
{
N z;
z.s[0]='3';
z.s[1]='4';
z.s[2]='j';
z.s[3]='k';
printf("f=%f\n",z.x);
return 0;
}
The output of the program above is: f=283135145630880207619489792.000000 , a number that is much larger than a float variable can store; the output should be, in scientific notation, 4.1977085E-8.
So what's wrong?
z.s={'3','4','j','k'}; would assign one array to another. C doesn't permit that, though you could declare the second and memcpy to the first.
The largest finite value that a single-precision IEEE float can store is 3.4028234 × 10^38, so 283135145630880207619489792.000000, which is approximately 2.8313514 × 10^26 is most definitely in range.
Assuming your chars are otherwise correct, the knee-jerk guess would be that you've got your endianness wrong.
EDIT:
34jk if taken from left to right, as on a big-endian machine is:
0x33 0x34 0x6a 0x6b
= 0011 0011, 0011 0100, 0110 1010, 0110 1011
So:
sign = 0
exponent = 011 0011 0 = 102 (dec), or -25 allowing for offset encoding
mantissa = [1] 011 0100 0110 1010 0110 1011 = 11823723 / (2^23)
So the value would be about 4.2 × 10^-8, which is what you want.
In little endian:
0x6b 0x6a 0x34 0x33
= 0110 1011, 0110 1010, 0011 0100, 0011 0011
sign = 0
exponent = 110 1011 0 = 214 (dec) => 87
mantissa = [1]110 1010 0011 0100 0011 0011 = 15348787 / (2^23)
So the value would be about 2.8 * 10^26, which is what your program is outputting. It's a safe conclusion you're on a little endian machine.
Summary then: byte order is different between machines. You want to use your bytes the other way around — try kj43.
What you actually see is {'k' 'j' '4' '3'}

Output Explanation of this program in C?

I have this program in C:
int main(int argc, char *argv[])
{
int i=300;
char *ptr = &i;
*++ptr=2;
printf("%d",i);
return 0;
}
The output is 556 on little endian.
I tried to understand the output. Here is my explanation.
Question is Will the answer remains the same in the big endian machine?
i = 300;
=> i = 100101100 //in binary in word format => B B Hb 0001 00101100 where B = Byte and Hb = Half Byte
(A)=> in memory (assuming it is Little endian))
0x12345678 - 1100 - 0010 ( Is this correct for little endian)
0x12345679 - 0001 - 0000
0x1234567a - 0000 - 0000
0x1234567b - 0000 - 0000
0x1234567c - Location of next intezer(location of ptr++ or ptr + 1 where ptr is an intezer pointer as ptr is of type int => on doing ++ptr it will increment by 4 byte(size of int))
when
(B)we do char *ptr = &i;
ptr will become of type char => on doing ++ptr it will increment by 1 byte(size of char)
so on doing ++ptr it will jump to location -> 0x12345679 (which has 0001 - 0000)
now we are doing
++ptr = 2
=> 0x12345679 will be overwritten by 2 => 0x12345679 will have 00*10** - 0000 instead of 000*1* - 0000
so the new memory content will look like this :
(C)
0x12345678 - 1100 - 0010
0x12345679 - 0010 - 0000
0x1234567a - 0000 - 0000
0x1234567b - 0000 - 0000
which is equivalent to => B B Hb 0010 00101100 where B = Byte and Hb = Half Byte
Is my reasoning correct?Is there any other short method for this?
Rgds,
Softy
In a little-endian 32-bit system, the int 300 (0x012c) is typically(*) stored as 4 sequential bytes, lowest first: 2C 01 00 00. When you increment the char pointer that was formerly the int pointer &i, you're pointing at the second byte of that sequence, and setting it to 2 makes the sequence 2C 02 00 00 -- which, when turned back into an int, is 0x22c or 556.
(As for your understanding of the bit sequence...it seems a bit off. Endianness affects byte order in memory, as the byte is the smallest addressable unit. The bits within the byte don't get reversed; the low-order byte will be 2C (00101100) whether the system is little-endian or big-endian. (Even if the system did reverse the bits of a byte, it'd reverse them again to present them to you as a number, so you wouldn't notice a difference.) The big difference is where that byte appears in the sequence. The only places where bit order matters, is in hardware and drivers and such where you can receive less than a byte at a time.)
In a big-endian system, the int is typically(*) represented by the byte sequence 00 00 01 2C (differing from the little-endian representation solely in the byte order -- highest byte comes first). You're still modifying the second byte of the sequence, though...making 00 02 01 2C, which as an int is 0x02012c or 131372.
(*) Lots of things come into play here, including two's complement (which almost all systems use these days...but C doesn't require it), the value of sizeof(int), alignment/padding, and whether the system is truly big- or little-endian or a half-assed implementation of it. This is a big part of why mucking around with the bytes of a bigger type so often leads to undefined or implementation-specific behavior.
This is implementation defined. The internal representation of an int is not known according to the standard, so what you're doing is not portable. See section 6.2.6.2 in the C standard.
However, as most implementations use two's complement representation of signed ints, the endianness will affect the result as described in cHaos answer.
This is your int:
int i = 300;
And this is what the memory contains at &i: 2c 01 00 00
With the next instruction you assign the address of i to ptr, and then you move to the next byte with ++ptr and change its value to 2:
char *ptr = &i;
*++ptr = 2;
So now the memory contains: 2c 02 00 00 (i.e. 556).
The difference is that in a big-endian system in the address of i you would have seen 00 00 01 2C, and after the change: 00 02 01 2C.
Even if the internal rappresentation of an int is implementation-defined:
For signed integer types, the bits of the object representation shall
be divided into three groups: value bits, padding bits, and the sign
bit. There need not be any padding bits; signed char shall not have
any padding bits. There shall be exactly one sign bit. Each bit that
is a value bit shall have the same value as the same bit in the object
representation of the corresponding unsigned type (if there are M
value bits in the signed type and N in the unsigned type, then M ≤ N).
If the sign bit is zero, it shall not affect the resulting value. If
the sign bit is one, the value shall be modified in one of the
following ways: — the corresponding value with sign bit 0 is negated
(sign and magnitude); — the sign bit has the value −(2M) (two’s
complement); — the sign bit has the value −(2M − 1) (ones’
complement). Which of these applies is implementation-defined, as
is whether the value with sign bit 1 and all value bits zero (for the
first two), or with sign bit and all value bits 1 (for ones’
complement), is a trap representation or a normal value. In the case
of sign and magnitude and ones’ complement, if this representation is
a normal value it is called a negative zero.
I like experiments and that's the reason for having the PowerPC G5.
stacktest.c:
int main(int argc, char *argv[])
{
int i=300;
char *ptr = &i;
*++ptr=2;
/* Added the Hex dump */
printf("%d or %x\n",i, i);
return 0;
}
Build command:
powerpc-apple-darwin9-gcc-4.2.1 -o stacktest stacktest.c
Output:
131372 or 2012c
Resume: the cHao's answer is complete and in case you're in doubt here is the experimental evidence.

Resources