Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

The printf() function is familiar, and its converters are often used when printing data, such as %c,%d,%f, etc. So why is it necessary to print a conversion? Can’t the system recognize it automatically? What is the purpose of converters?

Here is a simple example to look at the meaning of the existence of the conversion character, in the 8-bit MCU through different conversion character print two integers.

Prints two integers, a positive integer and a negative integer, through different converters. The core code is as follows:

short num1 = Awesome!;
short num2 = - 32700.;

 printf( "num1: short --- %6hd, unsigned short --- %6hu \r\n", num1, num1 );
 printf( "num2: short --- %6hd, unsigned short --- %6hu \r\n", num2, num2 );

 printf( "num1: int --- %6d, char --- %6c \r\n", num1, num1 );
 printf( "num2: int --- %6d, char --- %6c \r\n\r\n\r\n", num2, num2 );
Copy the code

Before printing the result, take a look at the range of data for short.

In the 8-bit microcontroller, the range of short data type is -32768 to 32768. The positive integer in the program is 666, and the negative integer is -32700, indicating that the data in the program are in the range of valid data.

Here’s what the program prints

When num1 is printed with short and unsigned short, the output is correct. But num2 printed with an unsigned short yields 32836. When two numbers are printed in the character type, a character appears. So where do these typo results come from? Why is this the result?

So let’s see how these two numbers are stored in memory.

By directly checking the memory of the MCU, it can be found that the number 666 is stored at the beginning of 0x00000C and the number -32700 is stored at the beginning of 0x00000E. The type of short is 16 bits, accounting for two bytes. So in memory, 02 and 9A are the numbers 666,80 and 44 are the numbers -32700.

Now what is the hexadecimal number that corresponds to these two numbers?

The source, inverse, and complement of the hexadecimal number 666 are 029A.

The number -32700 corresponds to a hexadecimal complement of 8044. Does the negative integer store its complement in memory?

Indeed, for signed integers, the system is represented by the complement of binary. The numbers 0-32767 represent themselves, while the numbers 32768-65535 represent negative numbers, where 65535 means -1,65534 means -2. Therefore, the saved number corresponding to -32700 is 65536-32767=32836.

As can be seen from the base conversion, 32836 corresponds to the hexadecimal 8044.

When the printf function is told to print an unsigned decimal integer by the conversion “%hu,” the system detects that the memory 32836 is in the range of unsigned integers, so it prints directly. When the conversion “%hd” tells the printf function to print a signed decimal integer, the system detects that 32836 in memory is greater than 32767, the maximum value of the signed integer. The system knows that the number must be negative and stored in inverse code. Then the correct value should be -(65536-32836)=-32700. So the “%hd” conversion prints -32700, and “%hu” prints 32836.

What about the character printed by “%c”? When “%c” is used in the system, it is considered to be a character device, and its value range is an ASCII table, 0 to 255. If the value exceeds 255, the system will use this number to mod 256, which is the last byte of only two bytes in memory.

So when the system prints the number 666 through ‘%c’, it will fetch only 9A in memory, and when it prints -32700, it will fetch only 44. What is the ASCII code table for these two numbers?

The corresponding character of 9a is š, and the corresponding character of 44 is D, but the corresponding character of 9a cannot be printed on the serial tool, so what is printed? 44 corresponds to the letter D, so it’s going to print the letter D.

As you can see from the above example, when printf() reads data in memory, it uses the converter to determine how many bits of data to read and from which location in memory. If the converter is set incorrectly, the printf() function will read the data in memory incorrectly.

Here is a simple example of the test code:

int n1=0x0102;
int n2=0x0304;
int n3=0x0506;
int n4=0x0708;

printf( "%ld,%ld,%ld,%ld \r\n\r\n\r\n", n1,n2,n3,n4 );
Copy the code

This is a simple example that defines four integers, each of which is two bytes, but prints the data using “%ld”, which is four bytes per character. The following output is displayed:

The printed numbers are very large, so where do they come from? Go straight to memory.

In memory, it can be seen that 01 to 08 are stored in memory in sequence. If “%ld” is used to print, then the print() function will directly fetch four bits from memory as the value of N1 when reading the value of N1, that is, the value of n1 is 0x01020304. You can use the base conversion to check.

16909060 converts to your hexadecimal system and sure enough it is 01020304

The corresponding value for n2 is 0x05060708

84281096 converted to hexadecimal and sure enough it was 05060708

It is obvious from this example that the conversion is very important for the printf() function to read data. If the conversion is not set correctly, the data read is bound to be wrong.