More to come...
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
6.3KB

  1. #ifndef CHAPTER_3_HEADER
  2. #define CHAPTER_3_HEADER
  3. #include <stdarg.h>
  4. #include "chapter_0.h"
  5. #include "chapter_1.h"
  6. /*
  7. Now, time has come to talk about (sadly) the most important standard library function in C programming language. Function 'printf' is like some semi-drunk old man, stumbling on
  8. the streets, talking to himself, talking to other people who try to ignore him, but if you stand and talk to him, he'll sometimes start talking about random things, and sometimes
  9. he'll stay on-topic. So, what does function 'printf' do? Essentially, it'll print formatted string to standard output, formatted in this case means that in the main string (first
  10. argument to 'printf' function) there are special characters that'll accept variadic arguments, format them (convert them to string in special manner) and output that final string.
  11. It's located in <stdio.h> header file, and it's commonly used through-out most C programs. You can learn more about it from manual pages with command 'man 3 printf', and below you
  12. can see some variations of that function. I just realigned them and added 'extern', which C assumes as default (but some compilers would generate a warning for that). We'll learn
  13. to implement variadic functions as well in this chapter, but I consider that a bad practice, even tho they can be extremely useful. Functions we learned to declare and define so
  14. far have constant and limited amount of arguments, while variadic functions can have any amount of arguments greater than one, as you can see below.
  15. @C
  16. extern int printf ( const char * format, ...);
  17. extern int sprintf (char * str, const char * format, ...);
  18. extern int snprintf (char * str, size_t size, const char * format, ...);
  19. extern int fprintf (FILE * stream, const char * format, ...);
  20. extern int dprintf (int fd, const char * format, ...);
  21. extern int vprintf ( const char * format, va_list ap);
  22. extern int vsprintf (char * str, const char * format, va_list ap);
  23. extern int vsnprintf (char * str, size_t size, const char * format, va_list ap);
  24. extern int vfprintf (FILE * stream, const char * format, va_list ap);
  25. extern int vdprintf (int fd, const char * format, va_list ap);
  26. @
  27. Why do I dislike them? Because they're implemented with the help of macros from <stdarg.h> header file, in not particularly good way, and I dislike using 'va_*' because I simply
  28. don't like the naming style. Also, you can't properly verify that you won't cause tiny bugs in your program, so before writing 'printf' function, you need to think, which is
  29. something I don't like to do mostly. For example, if you pass a 'float' type variable to 'printf' function, with "%f" in its' format, you need to convert it to 'double', because
  30. that 'va_arg' macro accepts only fully promoted types, which we'll explain below. All that aside, they can be very useful in specific cases!
  31. You'd use it something like these few examples below, and it's a good practise not to mix 'printf' with 'write' functions (if you're using both of them), because they're
  32. synchronized differently. Because of how they're buffered, mixing them can (in some cases) end up with mixed output.
  33. @C
  34. // And ignore the alignment here, it's just an usage example. You can put all of this into 'main' function in some temporary file, compile it and run it.
  35. int a = 1; printf ("Integer: %d\n", a);
  36. char b = 'A'; printf ("Character: %c\n", b);
  37. char * c = "Heyo"; printf ("%s world!\n", c);
  38. float d = 1.0F; printf ("%f\n", (double) d);
  39. double e = 1.0; printf ("%f\n", e);
  40. uint32_t f = 0X0011AAU; printf ("0X%X\n", f);
  41. size_t g = sizeof (a); printf ("%ul\n", g);
  42. int * h = & a; printf ("\t%p", (void *) h);
  43. printf ("\nCyaa world!\n" );
  44. // You can have more than one arguments, like this:
  45. printf ("%s world!\n Our integer is %d.\n Our character is '%c'.\nCyaa world!\n", c, a, b);
  46. // Never do this:
  47. char * message = "Heyo world!";
  48. printf (message);
  49. // Instead:
  50. char * message = "Heyo world!";
  51. printf ("%s", message);
  52. // Or:
  53. printf ("Heyo world!");
  54. @
  55. Now, don't get scared, C is old language, and I'm aware that this looks like someone made a joke 50 years ago and nobody understood it, they took it serious. In the end, variadic
  56. argument list type 'va_list' is black magic, as well as other 'va_*' stuff. Since 'va_arg' takes only fully promoted types, we're left with types 'int', 'double', 'void *' and
  57. 'char *'. Also, keep in mind that you don't need more arguments in 'printf' function, only that "const char * fmt" needs to be there always. It may take some time to get used to
  58. this function, but you can use it for very quick and easy debugging. If you're not sure what value your variables hold at some point, you can just print it there.
  59. I'll show you how to implement variadic argument functions, and we'll use these in few places, but I'm still not a big fan of them. Of course, we won't implement everything that
  60. 'printf' function from standard library has, or any more of its' alternatives, just these three below. However, we'll add few small additions, like colouring text with ASCII
  61. escape sequences and doing some basic formatting. Once you learn C better, you should take a look at manual pages for functions that you find interesting. Or even better, instead
  62. of reading stuff, try to implement them yourself, it's the best approach to learning anything.
  63. If you wonder why did we cover curses in chapter two, and printing in chapter three, which is simpler and shorter essentially, it's because curses use few "black magic" functions
  64. from header file <termios.h>, while printing uses <stdarg.h>, which is more difficult to use. We'll use it more in later chapters, but only as a show-case.
  65. */
  66. extern void print ( char * format, ...); // Notice the "...", which means it can accept any amount of arguments after that 'format'.
  67. extern void file_print (int file, char * format, ...); // Same as above, but it won't print to terminal, it'll write it to a file descriptor instead.
  68. extern void string_print (char * string, char * format, ...); // Same as above again, but it'll copy it to some string.
  69. #endif