Future Review
We learned earlier that the byte sequence of data storage differs depending on the schema of the CPU's End mode
Be Big-endian big-end mode , the most intuitive byte-order address low storage value of high, address high storage value of low , data filling, do not consider the corresponding relationship, just to the memory address from left to right according to the order from low to high, The values are written in the order of the usual highs to lows, in contrast to a byte-by-byte padding.
LE Little-endian Small End mode , then the most consistent with the human mind of the byte order, address low storage value of the low, address high storage value of the high , this is more in line with the human thinking of the byte order, because from the first impression of people, low value small, Should be placed in a small memory address, that is, low memory address, conversely, high value should be placed in the memory address of the place, that is, high memory address
Any data is stored in binary form in memory.
Specific reference to deep understanding of computer systems--numerical storage (i)-cpu big and small end mode
Then we discussed how to use the program to print out the form of the variable in memory, specific reference to the in-depth understanding of computer systems--numerical Storage (ii) –C program to print the variables of each byte or bit
Then we understand the original code, the inverse code, complement and shift code representation, specific reference to the in-depth understanding of computer systems--numerical Storage (iii) – original code, inverse code, complement and move code to explain integer data Overview of the integer
An arithmetic type that represents integers, characters, and Boolean values in the C + + language is called an integral type.
There are two types of characters: char and wchar_t.
The char type ensures that there is enough space to store a numeric value corresponding to any character in the machine's basic character set, so the char type is usually a single machine byte.
wchar_t types are used to extend character sets, such as Chinese characters, and some characters in these character sets cannot be represented by a single char.
The short, int, and long types all represent integer values, with different sizes of storage space. Generally, the short type is half the machine word length, the int type is a machine word length, and the long type is one or two machine words (in 32-bit machines, the int type and long type are usually the same word length).
The bool type represents the truth value true and false. You can assign any value of an arithmetic type to a bool object. A 0-valued arithmetic type represents false, and any value other than 0 represents true. Signed and unsigned types
In addition to the bool type, an integral type can be either signed (signed) or unsigned
(unsigned). As the name suggests, a signed type can represent a positive number or a negative number (including 0).
Unsigned types can only represent numbers greater than or equal to 0.
integer int, short, and long are signed by default. To obtain an unsigned type, you must specify that the type is unsigned, such as unsigned long. The unsigned int type can be abbreviated to unsigned, that is to say, unsigned without other type specifiers means unsigned int.
Unlike other integers, Char has three different types: plain char, unsigned char, and signed Char. Although Char has three different types, there are only two ways to represent it. You can use unsigned char or signed char to represent the char type. Which char representation is used is determined by the compiler. integers are stored in complement form Sample Programs
So now we have the details. We use the following procedure to see how an integer behaves in memory.
#include <stdio.h> #include <stdlib.h> int check_end () {int i = 0x12345678;
Char *c = (char *) &i;
return (*c = = 0x12);
int print_bit (void *addr, int size) {unsigned char *ptr = (unsigned char *) addr;
int print_bytes = 0;
if (ptr = NULL) {return-1;
for (print_bytes = 0;
Print_bytes < size;
print_bytes++, ptr++) {#ifdef DEBUG printf ("byte%d, data =%02x-=>", Print_bytes, *ptr);
#endif for (int print_bits = 7;
Print_bits >= 0;
print_bits--) {printf ("%d", (*ptr >> print_bits) & 1);
#ifdef DEBUG printf ("\ n");
#endif} printf ("\ n");
return print_bytes;
int print_byte (void *addr, int size) {unsigned char *paddr = (unsigned char *) addr;
int print_bytes = 0;
if (paddr = = NULL) {return-1; while (Print_bytes < size) {printf ("%02x", *paddR);
paddr++;
print_bytes++;
printf ("\ n");
return print_bytes;
int main (void) {if (check_end () = = 1) {printf ("big-endian mode \ \");
else {printf ("small end mode \ n");
int a = 1;
Print_bit (void *) &a, sizeof (a));
int b =-1;
Print_bit (void *) &b, sizeof (b)); /* X = -00101011 = 0x0000002b [x] original = 10000000 00000000 00000000 00101011 [X] anti = 11111111 11111111 11111111 11010100 [x] complement =
11111111 11111111 11111111 11010101 [x] Shift =01010101 */int X = 0x2b;
Print_bit (void *) &x, sizeof (x));
int y = -0x2b;
Print_bit (void *) &y, sizeof (y));
return exit_success;
}
Program Analysis
Whether it's the original code, the inverse code or the complement, the method of indicating positive numbers is consistent.
But there is a difference in negative numbers.
X = -0x2b = -00101011 = 0x0000002b
[X] original = 10000000 00000000 00000000 00101011 = 0x8000002b
[X] anti = 11111111 11111111 11111111 11010100 = 0xffffffd4
[X] complement = 11111111 11111111 11111111 11010101 = 0xffffffd5
[X] Move =01010101
Because our Intel CPU architecture uses the small end mode, then uses the complement to store, the 0XFFFFFFD5 uses the small end mode to store down, the low byte to the high byte data arranges is 0x D5 FF FF FF, namely 11010101 11111111 11111111 11111111
Visible, integers are representations of overflow integer values stored in memory in the form of complement
In an unsigned type, all bits represent values. If you define a type in a machine that uses 8-bit representations, then this type of unsigned type can take a value of 0 to 255.
The C + + standard does not define how the signed type is represented by bits, but rather by the freedom of each compiler to decide how to represent the signed type. These representations affect the range of values for the signed type.
The value of the 8-bit signed type must be at least from 127 to 127, but there are also many implementations that allow values from-128 to 127.
The most common strategy for representing signed integer types is to use one of the bits as a symbol bit. The sign bit is 1, the value is negative, the sign bit is 0, and the value is 0 or positive. A signed integer value is from-128 to 127.
For unsigned types, the compiler must adjust the bounds to meet the requirements. The compiler will model the value for the number of possible values for the unsigned type, and then take the resulting value. For example, the 8-bit unsigned char has a range of values ranging from 0 to 255 (including 255). If you assign a value that is beyond this range, the compiler will take the value after the 256 modulo value (its essence is that the extra bits are truncated and discarded).
For example, if you attempt to store 336 in a 8-bit unsigned char, the actual assignment is 80, because 80 is the value after modulo 336.
The high byte carry 1 is truncated and discarded, leaving the 80=336-256
For unsigned types, negative numbers always exceed their range of values. Objects of type unsigned may never save negative numbers. In some languages, it is illegal to assign a negative number to the unsigned type, but it is legal in C/s + +.
In C + +, assigning negative values to unsigned objects is perfectly legal, and the result is
This negative number is the value of the value after the modulus of the type is evaluated.
So, if you assign 1 to the 8-bit unsigned char, the result is 255, because 255 is the value after 1 modulo 256.
The same is truncated,-1 with the complement to store, high overflow bit is truncated, the remaining is 255
When you assign a value that exceeds the range of values to the signed type, the compiler determines the actual assigned value. In practice, many compilers handle signed types in the same way as unsigned types. That is, when the value is assigned, the value is taken after the value is evaluated for the value of the type. However, we cannot guarantee that the compiler will handle the signed type in this way.
#include <stdio.h> #include <stdlib.h> int check_end () {int i = 0x12345678;
Char *c = (char *) &i;
return (*c = = 0x12);
int print_bit (void *addr, int size) {unsigned char *ptr = (unsigned char *) addr;
int print_bytes = 0;
if (ptr = NULL) {return-1;
for (print_bytes = 0;
Print_bytes < size;
print_bytes++, ptr++) {#ifdef DEBUG printf ("byte%d, data =%02x-=>", Print_bytes, *ptr); #endif
for (int print_bits = 7;
Print_bits >= 0;
print_bits--) {printf ("%d", (*ptr >> print_bits) & 1);
#ifdef DEBUG printf ("\ n");
#endif} printf ("\ n");
return print_bytes;
int print_byte (void *addr, int size) {unsigned char *paddr = (unsigned char *) addr;
int print_bytes = 0;
if (paddr = = NULL) {return-1;
while (Print_bytes < size) {printf ("%02x", *paddr); paddr++;
print_bytes++;
printf ("\ n");
return print_bytes;
int main (void) {printf ("%d\n", sizeof (long));
unsigned char a = 336;
Print_bit (void *) &a, sizeof (char));
Print_bit (void *) &a, sizeof (short));
printf ("%d\n", a);
int *p = &a;
Print_bit (void *) p, sizeof (int));
printf ("%d\n", *p);
unsigned char b =-1;
Print_bit (void *) &b, sizeof (b));
printf ("%d\n", b);
Union A {Short ia;
unsigned char ca[2];
}AA;
Aa.ia = 336;
printf ("%d%d\n", aa.ca[0], aa.ca[1]);
Aa.ca[0] = 336;
printf ("%d%d\n", aa.ca[0], aa.ca[1]);
Aa.ca[1] = 336;
printf ("%d%d\n", aa.ca[0], aa.ca[1]);
return exit_success; }