At no time are the numbers in the computer in decimal format.
The question is not when do they become binary but when do they become decimal.
You should be able to take the binary number 0b1111011 and convert that to 123 decimal and 0x7B hexadecimal, not by using the base conversion buttons on your calculator but by understanding how conversion from bases works like 3785 seconds is 1 hour 3 minutes and 5 seconds (based 60 from base 10).
The C library sees you want decimal, it takes the bits 0b1111011 that up to that moment had no meaning they were just bits, and after this they will go back to having no meaning at least to the computer they only have meaning to you. To get the 100's place you have to divide by 0b1100100 the result of that is 0b1 right so subtract 0b1100100 from 0b1111011 and you get 0b10111, now divide that by 0b1010 you get 0b10 so subtract 0b10100 (0b1010 times 0b10) from 0b10111 and get 0b11 so the conversion to base 10 so far is 0b1, 0b10, 0b11. Now printf needs to make ASCII out of that so it adds 0b110000 to those numbers giving 0b110001, 0b110010, 0b110011. And feeds that "string" into a character output routine (and you see 123). At no time do we have anything decimal, its just bits being manipulated.
When you write some code
unsigned int x = 5;
The compiler converts that 5 (which is really 0b110101 in the source code file) into 0b101 and places that wherever it decides to store the variable x.
Now lets go back to 0b1111011 and convert that to hex, starting from the right take four bits at a time you get 0b111 and 0b1011, SIGNIFICANTLY faster than base 10 conversion thus far (in general, just a bit faster for an 8 bit number if that is what this was). One of two ways you can add 0b110000 then do a comparison with 0b111001 or you can do the comparison with 0b1001 then add a different number. So for example 0b111 becomes 0b110111 and then check if it is greater than 0b111001, nope, so move on to 0b1011 add 0b110000 and you get 0b111011 is that greater than 0b111001? yes it is so either add 0b111 or 0b100111 depending on whether you wanted to see capital letters or lower case, now the string is for example 0b110111,0b1000010 plus a terminating zero, you send that off to print and you see 7B on the output.
Hex output is going to be faster yes. How much and how relevant is that gain, it depends on a number of factors...
Now I have no idea what you mean by programming a source file in hex
unsigned int x = 0x5;
is going to take slightly longer to compile than
unsigned int x = 5;
Because of the extra characters. But
unsigned int x = 0x7B;
vs
unsigned int x = 123;
hmm, decimal probably still faster.
unsigned int x = 0x11111111;
vs
unsigned int x = 286331153;
Now you have to wonder, there is a point where the hex will be faster on a particular machine, data patterns matter too as shown here.
As shown here the hex version takes two more bytes of storage to hold the source file.
unsigned int x = 0x5;
unsigned int x = 5;
The compiled output is identical with respect to the constant being applied to x (0b101). So the machine code (and/or .data storage) is identical not only in size, but identical.
unsigned int fun0 ( void )
{
return(5);
}
unsigned int fun1 ( void )
{
return(0x5);
}
unsigned int fun2 ( void )
{
return(123);
}
unsigned int fun3 ( void )
{
return(0x7B);
}
gives this machine code
00000000 <fun0>:
0: e3a00005
4: e12fff1e
00000008 <fun1>:
8: e3a00005
c: e12fff1e
00000010 <fun2>:
10: e3a0007b
14: e12fff1e
00000018 <fun3>:
18: e3a0007b
1c: e12fff1e
There have been and are C libraries with %b, but it is non standard, never made sense why it isnt. Likewise octal, hmm, there is one for octal.
Note octal conversion is competitive with hex, you dont have the conditional
0b1111011, mask and shift off 3 bits at a time 0b001, 0b111, 0b011 add 0x110000 as you do each giving 0b110001, 0b110111, 0b110011. So you dont have the conditional but you have more "characters" to deal with, for 8 bit numbers hex could win but for larger octal should win.
while on that topic:
unsigned int fun0 ( void )
{
return(5);
}
unsigned int fun1 ( void )
{
return(0x5);
}
unsigned int fun2 ( void )
{
return(05);
}
unsigned int fun3 ( void )
{
return(123);
}
unsigned int fun4 ( void )
{
return(0x7B);
}
unsigned int fun5 ( void )
{
return(0173);
}
gives
00000000 <fun0>:
0: e3a00005
4: e12fff1e
00000008 <fun1>:
8: e3a00005
c: e12fff1e
00000010 <fun2>:
10: e3a00005
14: e12fff1e
00000018 <fun3>:
18: e3a0007b
1c: e12fff1e
00000020 <fun4>:
20: e3a0007b
24: e12fff1e
00000028 <fun5>:
28: e3a0007b
2c: e12fff1e
so in terms of "storage" of the source code 5 is cheaper than 05 is cheaper than 0x5 but 0x7B is the same as 0173 but 123 is cheaper. hex becomes cheapest as the numbers get larger (obviously it has a higher base 16 vs 8 vs 10).
Are you really that desperate for storage space of the source code? You need to be a tab person instead of a space person then. And use short variable names and function names. My long answer has probably filled up all of your ram.