In my opinion, the question asked in this comment is quite interesting and so I'd like first of all to rephrase such a questionit and then to provide a shortan answer followed by a longer oneand an example.
QUESTION – How can “the system”the system establish how one or more adjacent bytes have to be interpreted? In particular, how can “the system”the system establish whether a given bit patternsequence of bytes is a plain binary number or a 2's complement number?
SHORT ANSWER – “The system”The system establishes how to interpret a sequence of bytes through types. Types define
LONGER ANSWER – It takes some lines of C code and... well yes... again, it's always through types.
- how many bytes have to be considered
- how those bytes have to be interpreted
In the code belowEXAMPLE – Below we use an array of 4 unsigned char becauseassume that
- in most systems chars, signed and unsigned,
char's are stored in exacltly 1 byte long - we want to store raw bytes and therefore we
short's are not concerned about sign2 bytes long - the elements of an array
int's andfloat's are stored in contiguous memory locations - having 4 elements allows us to interpret the array contents according to types having different sizes in bytes long
The contents of the array have been worked out in order to avoid any endianness issue, i.e.Please note that these sizes are specific to run the same on big endian as well as on little endian machinesmy system.
In addition, the 8-bit values have been chosen so that 1-byte Although pretty common, 2-byte and 4-byte numbers are all negative - this means they have a leading 1can be different from system to system. This wayIf you're curious of what they are on your system, when using unsigned types (plain binary notation)use the value will be positive while when using signed types (2's complement notation) the value will be negativesizeof operator.
Standard C doesn't support binary literals so here we are using a gcc extension which allows to write integer constants as binary constants, consisting of a sequenceFirst of '0' and '1' digits, prefixed by '0b' or '0B' (see http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html#Binary-constants).
If you don't use gcc or your compiler doesn't support suchall we define an extension, you simply havearray containing 4 bytes and initialize all of them to substitute every binary literal in the array (i.e.binary number 0b1011110110111101) with its, corresponding to the hexadecimal literal (i.e.number 0xBDBD). By the way, the hex notation is the standard one when dealing with bits and/or bytes; here the binary notation has been preferred because it is (hopefully) clearer in this particular case.
#include// <stdio.h> intBD(hexadecimal) main = 10111101 ( binary) { unsigned char l_Just4Bytes[ 4 ] = { 0b101111010xBD, 0b101111010xBD, 0b101111010xBD, 0b101111010xBD }; Then we read the array content using different types.
###unsigned char and signed char###
// Reading memory 1 byte at a time: at first as unsigned char and then10111101 as signed char printf( "--- Reading memory 1 byte at a time ---------\n" ); printf( "sizeof( unsigned char ) = %d\n", sizeof( unsigned char ) ); printf( "sizeof( signed char ) = %d\n", sizeof( signed char ) ); printf( "l_Just4Bytes[ 0 ] as unsigned char -> %hi\n", l_Just4Bytes[ 0 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 0 ] ); // Data interpreted asnumber 2'Sequals COMPLEMENT189 printf( "l_Just4Bytes[ 1 ]"l_Just4Bytes as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n"%hi\n", *( signed char )l_Just4Bytes[ 1 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ]char* ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed charl_Just4Bytes )l_Just4Bytes[ 2 ] ); // Data interpreted10111101 as a 2'S COMPLEMENT printf( "l_Just4Bytes[ 3 ] as unsignednumber charequals -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY67 printf( "l_Just4Bytes[ 3 ]"l_Just4Bytes as signed char -> %i\n", *( ( signed charchar* )l_Just4Bytes[ 3l_Just4Bytes ]) ); // Data interpreted as 2'S COMPLEMENT ###unsigned short and short###
// Reading memory 2 bytes at a time: at first as unsigned short and then1011110110111101 as short printf( "--- Reading memory 2 bytes at a time --------\n" ); printf( "sizeof( unsigned short ) = %d\n", sizeof( unsigned short ) ); printf( "sizeof( short ) = %d\n",PLAIN sizeof(BINARY shortnumber )equals ); 48573 printf( "l_Just4Bytes[ 0..1 ]"l_Just4Bytes as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ]l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted1011110110111101 as a 2'S COMPLEMENT printf( "l_Just4Bytes[ 1..2 ] as unsignednumber shortequals -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY16963 printf( "l_Just4Bytes[ 1..2 ]"l_Just4Bytes as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ]l_Just4Bytes ) ); ###unsigned int, int and float###
// Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2..3 ]10111101101111011011110110111101 as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) ); //a DataPLAIN interpretedBINARY asnumber PLAINequals BINARY3183328701 printf( "l_Just4Bytes[ 2..3 ]"l_Just4Bytes as short unsigned int -> %hi\n"%u\n", *( ( short* )&l_Just4Bytes[unsigned 2int* ])l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT // Reading memory 4 bytes at a time: at first as unsigned int, then as int and eventually10111101101111011011110110111101 as float printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof(2'S unsignedCOMPLEMENT intnumber )equals );-1111638595 printf( "sizeof( int ) = %d\n","l_Just4Bytes sizeof(as int ) ); printf( "sizeof( foat ) = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n"%i\n", *( ( unsigned int* )&l_Just4Bytesl_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ]10111101101111011011110110111101 as int a IEEE 754 SINGLE-> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpretedPRECISION asnumber 2'Sequals COMPLEMENT-0.092647 printf( "l_Just4Bytes[ 0..3 ]"l_Just4Bytes as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } The values4 bytes in RAM (l_Just4Bytes[ 0..3 ]) always remain exactly the same throughout the whole program. The only thing that changes is how we interpret them.
WE
Again, we tell “the system” HOWthe system how to interpret them through TYPES. Types defines how many bytes have to be considered and how those bytes have to be interpretedtypes.
unsigned char: 11 byte in plain binarysigned char: 11 byte in 2's complementunsigned short: 2 bytes in plain binary notationshort: 22 bytes in 2's complementunsigned int: 44 bytes in plain binary notationint: 44 bytes in 2's complementfloat: 44 bytes in IEEE 754 single-precision notation
Please note that, although pretty common, the sizes mentioned above and used throughout the post are not the same on every system.
One last word. It can be interesting to change the array contents and then trying to make sense of the output; I wouldn't mind the float, though.
[EDIT] This post has been edited and its formatting has been revised after the comment by user4581301. Thank you for taking the time to drop those few helpful lines!