Skip to main content
deleted 3883 characters in body
Source Link
mw215
  • 323
  • 1
  • 9

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 weshort's are not concerned about sign2 bytes long
  • the elements of an arrayint's and float'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 binary
  • signed char: 11 byte in 2's complement
  • unsigned short: 2 bytes in plain binary notation
  • short: 22 bytes in 2's complement
  • unsigned int: 44 bytes in plain binary notation
  • int: 44 bytes in 2's complement
  • float: 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!

In my opinion, the question asked in this comment is quite interesting and so I'd like first of all to rephrase such a question and then to provide a short answer followed by a longer one.

QUESTION – How can “the system” establish how one or more bytes have to be interpreted? In particular, how can “the system” establish whether a given bit pattern is a plain binary number or a 2's complement number?

SHORT ANSWER – “The system” establishes how to interpret a sequence of bytes through types.

LONGER ANSWER – It takes some lines of C code and... well yes... again, it's always through types.

In the code below we use an array of 4 unsigned char because

  • in most systems chars, signed and unsigned, are stored in exacltly 1 byte
  • we want to store raw bytes and therefore we are not concerned about sign
  • the elements of an array are stored in contiguous memory locations
  • having 4 elements allows us to interpret the array contents according to types having different sizes in bytes

The contents of the array have been worked out in order to avoid any endianness issue, i.e. to run the same on big endian as well as on little endian machines.
In addition, the 8-bit values have been chosen so that 1-byte, 2-byte and 4-byte numbers are all negative - this means they have a leading 1. This way, when using unsigned types (plain binary notation) the value will be positive while when using signed types (2's complement notation) the value will be negative.

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 sequence 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 such an extension, you simply have to substitute every binary literal in the array (i.e. 0b10111101) with its corresponding hexadecimal literal (i.e. 0xBD). 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> int main (  ) { unsigned char l_Just4Bytes[ 4 ] = { 0b10111101, 0b10111101, 0b10111101, 0b10111101 };   // Reading memory 1 byte at a time: at first as unsigned char and then 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 as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1 ] as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] );  // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 1 ] );  // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 2 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 3 ] as unsigned char -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 3 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 3 ] ); // Data interpreted as 2'S COMPLEMENT  // Reading memory 2 bytes at a time: at first as unsigned short and then 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", sizeof( short ) );  printf( "l_Just4Bytes[ 0..1 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1..2 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1..2 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2..3 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) );  // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2..3 ] as short  -> %hi\n", *( ( short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as 2'S COMPLEMENT // Reading memory 4 bytes at a time: at first as unsigned int, then as int and eventually as float printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof( unsigned int ) ); printf( "sizeof( int ) = %d\n", sizeof( int ) ); printf( "sizeof( foat )  = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n", *( ( unsigned int* )&l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ] as int  -> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 0..3 ] as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } 

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered and how those bytes have to be interpreted.

  • unsigned char: 1 byte in plain binary
  • signed char: 1 byte in 2's complement
  • unsigned short: 2 bytes in plain binary notation
  • short: 2 bytes in 2's complement
  • unsigned int: 4 bytes in plain binary notation
  • int: 4 bytes in 2's complement
  • float: 4 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!

In my opinion, the question asked in this comment is quite interesting and so I'd like first of all to rephrase it and then to provide an answer and an example.

QUESTION – How can the system establish how one or more adjacent bytes have to be interpreted? In particular, how can the system establish whether a given sequence of bytes is a plain binary number or a 2's complement number?

ANSWER – The system establishes how to interpret a sequence of bytes through types. Types define

  • how many bytes have to be considered
  • how those bytes have to be interpreted

EXAMPLE – Below we assume that

  • char's are 1 byte long
  • short's are 2 bytes long
  • int's and float's are 4 bytes long

Please note that these sizes are specific to my system. Although pretty common, they can be different from system to system. If you're curious of what they are on your system, use the sizeof operator.

First of all we define an array containing 4 bytes and initialize all of them to the binary number 10111101, corresponding to the hexadecimal number BD.

// BD(hexadecimal) = 10111101 (binary) unsigned char l_Just4Bytes[ 4 ] = { 0xBD, 0xBD, 0xBD, 0xBD }; 

Then we read the array content using different types.

###unsigned char and signed char###

// 10111101 as a PLAIN BINARY number equals 189 printf( "l_Just4Bytes as unsigned char -> %hi\n", *( ( unsigned char* )l_Just4Bytes ) );  // 10111101 as a 2'S COMPLEMENT number equals -67 printf( "l_Just4Bytes as signed char  -> %i\n", *( ( signed char* )l_Just4Bytes ) ); 

###unsigned short and short###

// 1011110110111101 as a PLAIN BINARY number equals 48573 printf( "l_Just4Bytes as unsigned short -> %hu\n", *( ( unsigned short* )l_Just4Bytes ) );   // 1011110110111101 as a 2'S COMPLEMENT number equals -16963 printf( "l_Just4Bytes as short -> %hi\n", *( ( short* )l_Just4Bytes ) ); 

###unsigned int, int and float###

// 10111101101111011011110110111101 as a PLAIN BINARY number equals 3183328701 printf( "l_Just4Bytes as unsigned int -> %u\n", *( ( unsigned int* )l_Just4Bytes ) ); // 10111101101111011011110110111101 as a 2'S COMPLEMENT number equals -1111638595 printf( "l_Just4Bytes as int -> %i\n", *( ( int* )l_Just4Bytes ) );  // 10111101101111011011110110111101 as a IEEE 754 SINGLE-PRECISION number equals -0.092647 printf( "l_Just4Bytes as float  -> %f\n", *( ( float* )l_Just4Bytes ) ); 

The 4 bytes in RAM (l_Just4Bytes[ 0..3 ]) always remain exactly the same. The only thing that changes is how we interpret them.

Again, we tell the system how to interpret them through types.

  • unsigned char: 1 byte in plain binary
  • signed char: 1 byte in 2's complement
  • unsigned short: 2 bytes in plain binary notation
  • short: 2 bytes in 2's complement
  • unsigned int: 4 bytes in plain binary notation
  • int: 4 bytes in 2's complement
  • float: 4 bytes in IEEE 754 single-precision notation
 

[EDIT] This post has been edited after the comment by user4581301. Thank you for taking the time to drop those few helpful lines!

deleted 1 character in body
Source Link
mw215
  • 323
  • 1
  • 9

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered AND how those bytes have to be interpreted.
WE tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered and how those bytes have to be interpreted.

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered AND how those bytes have to be interpreted.

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered and how those bytes have to be interpreted.

Most of the code comments have been taking outside. Some paragraph have been shortened. Some formatting has been added in order to improve the readability of the post.
Source Link
mw215
  • 323
  • 1
  • 9
#include <stdio.h> int main ( ) { // Here we use // - the type unsigned char for two reasons // - in most systems chars, signed and unsigned, are stored in exacltly 1 byte; by the way, a byte is the minimum allocatable/addressable/manageable data unit // - we want to manipulate raw bytes and therefore, not being concerned about sign, we use unsigned chars; sign only comes into play when representing signed // numbers // - an array in order to store a few bytes in contiguous memory locations; using a corresponding number of unsigned char variables would not guarantee such a // condition. Contiguity is essential in order for the application to be able to correctly interpret multi-byte values such as shorts, ints and floats, since the // system always stores multi-byte values in contiguous memory locations // - 4 elements so that we can interpret the array contents according to types having a different size in bytes // // The contents of the array have been worked out in order to avoid any endianness issue, i.e. to run the same on big endian as well as on little endian machines. In // particular, this is accomplished by storing the same value (8 bits) in each of its elements. // In addition, to make this example clearer, the 8-bit values have been chosen so that 1-byte, 2-byte and 4-byte numbers are all negative - this means they have a // leading 1. This way, when using unsigned types (plain binary notation) the value will be positive while when using signed types (2's complement notation) the value // will be negative. // // 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 sequence // 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 such an extension, you simply have to substitute every binary literal in the array (i.e. 0b10111101) with its corresponding hexadecimal literal (i.e. // 0xBD). 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 from a 'didactic' point of view. // unsigned char l_Just4Bytes[ 4 ] = { 0xBD, 0xBD, 0xBD, 0xBD }; // Hex notation unsigned char l_Just4Bytes[ 4 ] = { 0b10111101, 0b10111101, 0b10111101, 0b10111101 }; // Binary notation // Reading memory 1 byte at a time: at first as unsigned char and then as signed char // Both unsigned char and char types are 1-byte long, therefore we can interpret each element of the array in any of these two ways 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 as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 1 ] as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 1 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 2 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 3 ] as unsigned char -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 3 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 3 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS // Reading memory 2 bytes at a time: at first as unsigned chars and then as integers (conversion by the printf formatting mechanism?) // Both unsigned short and short types are 2-byte long, therefore we can interpret two adjacent elements of the array in any of these two ways printf( "--- Reading memory 2 bytes at a time --------\n" ); printf( "sizeof( unsigned short ) = %d\n", sizeof( unsigned short ) ); printf( "sizeof( short ) = %d\n", sizeof( short ) ); printf( "l_Just4Bytes[ 0..1 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 1..2 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1..2 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 2..3 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2..3 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS // Reading memory 4 bytes at a time: at first as unsigned int, then as int (conversion by the printf formatting mechanism?) and eventually as float // unsigned int, int and float are all 4-byte long, therefore we can interpret the array contents in any of these three ways printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof( unsigned int ) ); printf( "sizeof( int ) = %d\n", sizeof( int ) ); printf( "sizeof( foat ) = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n", *( ( unsigned int* )&l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ] as int -> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 0..3 ] as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } 

In the code below we use an array of 4 unsigned char because

  • in most systems chars, signed and unsigned, are stored in exacltly 1 byte
  • we want to store raw bytes and therefore we are not concerned about sign
  • the elements of an array are stored in contiguous memory locations
  • having 4 elements allows us to interpret the array contents according to types having different sizes in bytes

The contents of the array have been worked out in order to avoid any endianness issue, i.e. to run the same on big endian as well as on little endian machines.
In addition, the 8-bit values have been chosen so that 1-byte, 2-byte and 4-byte numbers are all negative - this means they have a leading 1. This way, when using unsigned types (plain binary notation) the value will be positive while when using signed types (2's complement notation) the value will be negative.

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 sequence 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 such an extension, you simply have to substitute every binary literal in RAMthe array (l_Just4Bytes[ 0i.e.3 ] 0b10111101) with its corresponding hexadecimal literal (i.e. 0xBD). 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> int main ( ) { unsigned char l_Just4Bytes[ 4 ] = { 0b10111101, 0b10111101, 0b10111101, 0b10111101 }; // Reading memory 1 byte at a time: at first as unsigned char and then 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 as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1 ] as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 1 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 2 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 3 ] as unsigned char -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 3 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 3 ] ); // Data interpreted as 2'S COMPLEMENT // Reading memory 2 bytes at a time: at first as unsigned short and then 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", sizeof( short ) ); printf( "l_Just4Bytes[ 0..1 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1..2 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1..2 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2..3 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2..3 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as 2'S COMPLEMENT // Reading memory 4 bytes at a time: at first as unsigned int, then as int and eventually as float printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof( unsigned int ) ); printf( "sizeof( int ) = %d\n", sizeof( int ) ); printf( "sizeof( foat ) = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n", *( ( unsigned int* )&l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ] as int -> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 0..3 ] as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } 

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered AND how those bytes have to be interpreted.

For instance, above we have used the following types to interpret the contents of the l_Just4Bytesl_Just4Bytes array

  • unsigned charunsigned char: 1 byte in plain binary
  • signed charsigned char: 1 byte in 2's complement
  • unsigned shortunsigned short: 2 bytes in plain binary notation
  • shortshort: 2 bytes in 2's complement
  • unsigned intunsigned int: 4 bytes in plain binary notation
  • intint: 4 bytes in 2's complement
  • floatfloat: 4 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.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!

#include <stdio.h> int main ( ) { // Here we use // - the type unsigned char for two reasons // - in most systems chars, signed and unsigned, are stored in exacltly 1 byte; by the way, a byte is the minimum allocatable/addressable/manageable data unit // - we want to manipulate raw bytes and therefore, not being concerned about sign, we use unsigned chars; sign only comes into play when representing signed // numbers // - an array in order to store a few bytes in contiguous memory locations; using a corresponding number of unsigned char variables would not guarantee such a // condition. Contiguity is essential in order for the application to be able to correctly interpret multi-byte values such as shorts, ints and floats, since the // system always stores multi-byte values in contiguous memory locations // - 4 elements so that we can interpret the array contents according to types having a different size in bytes // // The contents of the array have been worked out in order to avoid any endianness issue, i.e. to run the same on big endian as well as on little endian machines. In // particular, this is accomplished by storing the same value (8 bits) in each of its elements. // In addition, to make this example clearer, the 8-bit values have been chosen so that 1-byte, 2-byte and 4-byte numbers are all negative - this means they have a // leading 1. This way, when using unsigned types (plain binary notation) the value will be positive while when using signed types (2's complement notation) the value // will be negative. // // 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 sequence // 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 such an extension, you simply have to substitute every binary literal in the array (i.e. 0b10111101) with its corresponding hexadecimal literal (i.e. // 0xBD). 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 from a 'didactic' point of view. // unsigned char l_Just4Bytes[ 4 ] = { 0xBD, 0xBD, 0xBD, 0xBD }; // Hex notation unsigned char l_Just4Bytes[ 4 ] = { 0b10111101, 0b10111101, 0b10111101, 0b10111101 }; // Binary notation // Reading memory 1 byte at a time: at first as unsigned char and then as signed char // Both unsigned char and char types are 1-byte long, therefore we can interpret each element of the array in any of these two ways 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 as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 1 ] as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 1 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 2 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 3 ] as unsigned char -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 3 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 3 ] ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS // Reading memory 2 bytes at a time: at first as unsigned chars and then as integers (conversion by the printf formatting mechanism?) // Both unsigned short and short types are 2-byte long, therefore we can interpret two adjacent elements of the array in any of these two ways printf( "--- Reading memory 2 bytes at a time --------\n" ); printf( "sizeof( unsigned short ) = %d\n", sizeof( unsigned short ) ); printf( "sizeof( short ) = %d\n", sizeof( short ) ); printf( "l_Just4Bytes[ 0..1 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 1..2 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1..2 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 2..3 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2..3 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS // Reading memory 4 bytes at a time: at first as unsigned int, then as int (conversion by the printf formatting mechanism?) and eventually as float // unsigned int, int and float are all 4-byte long, therefore we can interpret the array contents in any of these three ways printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof( unsigned int ) ); printf( "sizeof( int ) = %d\n", sizeof( int ) ); printf( "sizeof( foat ) = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n", *( ( unsigned int* )&l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ] as int -> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT <--- HERE IT IS printf( "l_Just4Bytes[ 0..3 ] as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } 

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered AND how those bytes have to be interpreted.

For instance, above we have used the following types to interpret the contents of the l_Just4Bytes array

  • unsigned char: 1 byte in plain binary
  • signed char: 1 byte in 2's complement
  • unsigned short: 2 bytes in plain binary notation
  • short: 2 bytes in 2's complement
  • unsigned int: 4 bytes in plain binary notation
  • int: 4 bytes in 2's complement
  • float: 4 bytes in IEEE 754 single-precision notation

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.

In the code below we use an array of 4 unsigned char because

  • in most systems chars, signed and unsigned, are stored in exacltly 1 byte
  • we want to store raw bytes and therefore we are not concerned about sign
  • the elements of an array are stored in contiguous memory locations
  • having 4 elements allows us to interpret the array contents according to types having different sizes in bytes

The contents of the array have been worked out in order to avoid any endianness issue, i.e. to run the same on big endian as well as on little endian machines.
In addition, the 8-bit values have been chosen so that 1-byte, 2-byte and 4-byte numbers are all negative - this means they have a leading 1. This way, when using unsigned types (plain binary notation) the value will be positive while when using signed types (2's complement notation) the value will be negative.

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 sequence 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 such an extension, you simply have to substitute every binary literal in the array (i.e. 0b10111101) with its corresponding hexadecimal literal (i.e. 0xBD). 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> int main ( ) { unsigned char l_Just4Bytes[ 4 ] = { 0b10111101, 0b10111101, 0b10111101, 0b10111101 }; // Reading memory 1 byte at a time: at first as unsigned char and then 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 as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1 ] as unsigned char -> %hi\n", l_Just4Bytes[ 1 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 1 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2 ] as unsigned char -> %hi\n", l_Just4Bytes[ 2 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 2 ] ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 3 ] as unsigned char -> %hi\n", l_Just4Bytes[ 3 ] ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 3 ] as signed char -> %i\n", ( signed char )l_Just4Bytes[ 3 ] ); // Data interpreted as 2'S COMPLEMENT // Reading memory 2 bytes at a time: at first as unsigned short and then 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", sizeof( short ) ); printf( "l_Just4Bytes[ 0..1 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..1 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 0 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 1..2 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 1..2 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 1 ] ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 2..3 ] as unsigned short -> %hu\n", *( ( unsigned short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 2..3 ] as short -> %hi\n", *( ( short* )&l_Just4Bytes[ 2 ] ) ); // Data interpreted as 2'S COMPLEMENT // Reading memory 4 bytes at a time: at first as unsigned int, then as int and eventually as float printf( "--- Reading memory 4 bytes at a time --------\n" ); printf( "sizeof( unsigned int ) = %d\n", sizeof( unsigned int ) ); printf( "sizeof( int ) = %d\n", sizeof( int ) ); printf( "sizeof( foat ) = %d\n", sizeof( float ) ); printf( "l_Just4Bytes[ 0..3 ] as unsigned int -> %u\n", *( ( unsigned int* )&l_Just4Bytes ) ); // Data interpreted as PLAIN BINARY printf( "l_Just4Bytes[ 0..3 ] as int -> %i\n", *( ( int* )&l_Just4Bytes ) ); // Data interpreted as 2'S COMPLEMENT printf( "l_Just4Bytes[ 0..3 ] as float -> %f\n", *( ( float* )l_Just4Bytes ) ); // Data interpreted as IEEE 754 single-precision return 0; } 

The values 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 tell “the system” HOW to interpret them through TYPES. Types defines how many bytes have to be considered AND how those bytes have to be interpreted.

For instance, above we have used the following types to interpret the contents of the l_Just4Bytes array

  • unsigned char: 1 byte in plain binary
  • signed char: 1 byte in 2's complement
  • unsigned short: 2 bytes in plain binary notation
  • short: 2 bytes in 2's complement
  • unsigned int: 4 bytes in plain binary notation
  • int: 4 bytes in 2's complement
  • float: 4 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!

Source Link
mw215
  • 323
  • 1
  • 9
Loading