In C, the program at the time of execution does not "know" anything about variable size - with some luck the compiler does not let your program compile. The printf
and scanf
although very used, are nicely coplexed constructions on top of the C to facilitate the input and output of somewhat more complex data types - but to use both is better (1) follow exactly the recipe you saw, or (2) read some complete documentation, and know exactly what’s going on.
In this case, among the printf and scanf data type codes, THERE IS NO markup for a 1 byte numeric type. The type "%c" is understood as a 1 byte type, but the function works with the number as text (that is, it is sent (or read) as 1 byte to the terminal, not its decimal representation from 0 to 255 (or -128 to 127)).
When using type %d to print a char variable as a number in the printf, this works because:
- The compiler knows, because of the definition of the printf, that it can by any kind of data as an argument to the printf that it "turns".
- However, the architecture of modern PC Cpus does not support placing values smaller than 4 bytes - equal to a native integer - on the stack to be passed as parameters - so the compiler converts their number into the "char" variable to an "int 32" (or 64, depends on the file and flags for the compiler), in passing arguments
- If it were a function with well-defined parameters that would accept a "char" or "unsigned char" argument from the side being called, the code generated for that function would take only the 8 least significant bits of the stack, and discard the 3 remaining bytes, and everyone would be happy.
- In the case of printf, it is not predefined what it will pick up from the stack - its programmed code is that it looks at the format string, and assumes that that data, in the order it appears, is in the stack passed as arguments. It sees a "%d", and picks up an integer ( 4 or 8 bytes) - as the compiler generated code to "stow" the "char" when placing it in the stack, this works well. If the printf finds a "%c", it takes the least significant byte of the stack, and inserts it "as it came into the world" into the stream it will print. (In the case of %d, the printf converts the number to the decimal representation into text of its value, before putting it into the string - that is, it has a "itoa" call or equivalent inside).
- In the case of scanf - the thing changes: Scanf sees a %d, and understands that it has a memory space of 4 (or 8) bytes to store the value, at the location indicated by the pointer passed. He reads a number in text, makes his internal call to
atoi
or equivalent, takes the return value - a native integer, and writes all bytes in the memory position
In your case, in the position of valor_1
. As with most architectures, numbers are written "little endian" - that is, the least significant byte goes in the lowest memory position, if you type "255" for the SCANF, it will generate the 32-bit integer that we can repress by "0x000000ff", and this "ff" goes in the exact position of the passsado pointer - the "valor_1", so the number appears there. Only the other 3 bytes with zero, will be stored in the following memory positions - the next at the position of valor_2
(depending on the compiler’s settings), and the other 2 over other variables, or other internal data used by the program. This is what generates the behavior you are seeing, and that only by a lucky chance does not generate a segmentation failure.
In modern operating systems, a segmentation failure at this point is just one error most imprinted on the terminal, and life goes on. But, for example, from windows 3 to XP, your chance that this simple program would give a blue screen and force the computer to re-launch was PRETTY big. And in a machine with a microcontroller, like Arduin, this same error can generate undefined behavior, even resulting in burning of components.
The C language is one of the most powerful tools for programming computers, and allows the raw use of the CPU to do whatever you want - however you should know what is being done. The use of C as the first programming language in many college programs and technicians, without first emphasizing architecture and how this data travels, and let students create programs with "printf" and "scanf" and erase the screen with "system(cls)" however is a mistake (*) - today higher level languages allow a creation of an input interface of numerical and textual data in a much simpler way, in which the learner can worry about logic, and not try to guess what is happening inside, functions that were created as shortcuts but have very dangerous gaps, when computers did not have enough memory for something more sophisticated. (that’s the printf, the scanf, and even the gets and puts). If you use Python, Java, Javascript will be able to do as many exercises with input and output data on the terminal until you learn well "if" "Else", functions, basic accounts, etc...without the danger of blowing up the computer ("explode" is figured on secure environments like Linux, Osx or newer Windows, but it may not be an Arduino)
TL;DR: It’s nice to try to play with the dice sizes and teach a lot of stuff, but it won’t work with scanf
-always pass a int
for the scanf - you can have variables unsigned char
and pass the value that came from the scanf to them with =
after the return, and use them with printf
without having too much trouble. If your goal however is only the logic of "if", functions, and accounts, leave the C for the "homework",and play with javascript, Python, PHP - will be able to do experiments and learn faster than if you have to literally worry about where every byte goes in memory, what happens to C programs.
(*) I am at the disposal of your teachers and course coordinators to talk about it if you want.
Giusepe, do you want to work with numbers or characters? You have declared variables as char, but you are using %d which is for integers.
– Daniel Mendes
The idea is to work with numbers between 0 and 255.
– user165693