Reputation: 11
I have a char of value say 0xB3, and I need to split this into two separate char's. So X = 0xB and Y = 0x3. I've tried the following code:
int main ()
{
char addr = 0xB3;
char *p = &addr;
printf ("%c, %c\n", p[0], p[1]); //This prints ?, Y
printf ("%X, %X\n", p[0], p[1]); //This prints FFFFFFB3, 59
return 0;
}
Just to clarify, I need to take any 2 byte char of value 00 to FF and split the first and second byte into separate char's. Thanks.
Upvotes: 0
Views: 7845
Reputation: 1083
Ok so your trying to split 0xB3 into 0xB and 0x3, Just for future reference don't say 'byte chars', The 2 parts of a byte are commonly known as 'Nibbles', a byte is made up of 2 nibbles (which are made up of 4 bits).
If you didn't know, char
refers to 1 byte.
So heres the problems with your code:
char addr = 0xB3; <---- Creates single byte with value 0xB3 - Good
char *p = &addr; <---- Creates pointer pointing to 0xB3 - Good
printf ("%c, %c\n", p[0], p[1]); <---- p[0], p[1] - Bad
printf ("%X, %X\n", p[0], p[1]); <---- p[0], p[1] - Bad
Ok so when your referring to p[0]
and p[1]
your telling your system that the pointer p
is pointing to an array of char
s (p[0] would refer to 0xB3 but p[1] would be going to the next byte in memory)
Example : This is something your system memory would look like (But with 8 byte pointers)
Integer Values Area Pointers Area
0x01 0x02 0x03 0x04 0x05 0x06 0x12 0x13 0x14 0x15 0x16
----------------------------- ------------------------
.... .... 0xB3 0x59 .... .... .... .... 0x03 .... ....
----------------------------- ------------------------
^ ^ ^
addr | p (example pointer pointing to example address 0x03)
Random number (Pointers are normally 8 Bytes but)
showing up in p[1] (But In this example I used single bytes)
So when you tell your system to get p[0]
or *p
(these would do the same thing)
it will go to the address (eg. 0x03) and get one byte (because its a char)
in this case 0xB3
.
But when you try p[1]
or *(p+1)
That will go to the address (eg. 0x03) skip the first char
and get the next one giving us 0x59
which would be there for some other variable.
Ok so we've got that out of the way so how do you get the nibbles?
A problem with getting the nibble is that you generally can't just have half a byte a put variable, theres no type that supports just 4 bits. When you print with %x/%X it will only show the nibbles up to the last non-zero number eg. = 0x00230242 would only show 230242 but if you do something like
%2lX would show 2 full bytes (including the zeros) %4lX would show 4 full bytes (including the zeros)
So it is pretty pointless trying to get individual nibbles but if you want way to do something like then do:
char addr = 0x3B;
char addr1 = ((addr >> 4) & 0x0F);
char addr2 = ((addr >> 0) & 0x0F);
Upvotes: 0
Reputation: 49463
There's quite a few problems here, let's take a look at your code:
int main ()
{
char addr = 0xB3; <-- you're asigning 0xB3 in hex, which is (179 in dec) to addr
char *p = &addr; <-- you're assigning a pointer to point to addr
If addr
were unsigned, it would now be set to 179, the extended ASCII of │ ( Box drawing character )
A char
value can be -127 to +127 if it's signed, or 0 to 255 if it's unsigned. Here (according to your output) it's signed, so you're overflowing the char
with that assignment.
printf ("%c, %c\n", p[0], p[1]); <-- print the char value of what p is pointing to
also, do some UB
printf ("%X, %X\n", p[0], p[1]); <-- print the hex value of what p is pointing to
also, do some UB
So the second part of your code here prints the char value of your overflowed addr
var, which happens to print '?'
for you. The hex value of addr
is FFFFFFB3
indicating you have a negitive value (upper most bit is the signed bit).
This: p[0]
is really an "add and deference" operator. Meaning that we're going to take the address of p
, add 0
to it, then deference and look at the result:
p ---------+
V
------------------------------------------
| ptr(0xB3) | ? | ? | ... |
-------------------------------------------
0xbfd56c2b 0xbfd56c2C 0xbfd56c2d ...
When you do p[1]
this goes one char
or one byte past ptr and gives you that result. What's there? Don't know. That's out of your scope:
p+1 -------------------+
V
------------------------------------------
| ptr(0xB3) | ? | ? | ... |
-------------------------------------------
0xbfd56c2b 0xbfd56c2C 0xbfd56c2d ...
Y
's ASCII value (in hex) is 0x59, so behind your pointer in memory was a Y
. But it could have been anything, what is was going to do was undefined. A correct way to do this would be:
int main ()
{
unsigned char addr = 0xB3;
char low = addr & 0x0F;
char high = (addr >> 4) & 0x0F;
printf("%#x becomes %#x and %#x\n", addr, high, low);
return 0;
}
This works via:
0xB3 => 1011 0011 0xB3 >> 4 = 0000 1011
& 0000 1111 & 0000 1111
------------ -------------
0000 0011 => 3 low 0000 1011 => B high
Upvotes: 1
Reputation: 133609
Why do you need to pass by a pointer? just take the 4 relevant bits and shift the most significative when needed:
char lower = value & 0x0F;
char higher = (value >> 4) & 0x0F;
Then 0xB3
is a single byte, not two bytes. Since a hex digit can have 16 values two digits can store 16*16 = 256 values, which is how much you can store in a byte.
Upvotes: 0
Reputation: 363737
Straight from the Wikipedia:
#define HI_NIBBLE(b) (((b) >> 4) & 0x0F)
#define LO_NIBBLE(b) ((b) & 0x0F)
So HI_NIBBLE(addr)
would be 0xB
. However, 0x00
through 0xFF
are not "double bytes". They're single-byte values. A single hex digit can take on 16 bytes, while a byte can take on 256 = 16² of them, so you need two hex digits to represent arbitrary byte values.
Upvotes: 5