2023-11-13 21:03:29 -05:00
/*
Copyright ( c ) 2023 : Ognjen ' xolatile ' Milan Robovic
Xhartae is free software ! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation .
And when you do redistribute it or modify it , it will use either version 3 of the License , or ( at yours truly opinion ) any later version .
It is distributed in the hope that it will be useful or harmful , it really depends . . . But no warranty what so ever , seriously . See GNU / GPLv3 .
*/
# ifndef CHAPTER_3_HEADER
# define CHAPTER_3_HEADER
# include <stdarg.h>
# include "chapter_0.h"
# include "chapter_1.h"
/*
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
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
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
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 .
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
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
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
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 .
@ C
extern int printf ( const char * format , . . . ) ;
extern int sprintf ( char * str , const char * format , . . . ) ;
extern int snprintf ( char * str , size_t size , const char * format , . . . ) ;
extern int fprintf ( FILE * stream , const char * format , . . . ) ;
extern int dprintf ( int fd , const char * format , . . . ) ;
extern int vprintf ( const char * format , va_list ap ) ;
extern int vsprintf ( char * str , const char * format , va_list ap ) ;
extern int vsnprintf ( char * str , size_t size , const char * format , va_list ap ) ;
extern int vfprintf ( FILE * stream , const char * format , va_list ap ) ;
extern int vdprintf ( int fd , const char * format , va_list ap ) ;
@
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
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
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
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 !
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
synchronized differently . Because of how they ' re buffered , mixing them can ( in some cases ) end up with mixed output .
@ C
// 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.
int a = 1 ; printf ( " Integer: %d \n " , a ) ;
char b = ' A ' ; printf ( " Character: %c \n " , b ) ;
char * c = " Heyo " ; printf ( " %s world! \n " , c ) ;
float d = 1.0F ; printf ( " %f \n " , ( double ) d ) ;
double e = 1.0 ; printf ( " %f \n " , e ) ;
uint32_t f = 0 X0011AAU ; printf ( " 0X%X \n " , f ) ;
size_t g = sizeof ( a ) ; printf ( " %ul \n " , g ) ;
int * h = & a ; printf ( " \t %p " , ( void * ) h ) ;
printf ( " \n Cyaa world! \n " ) ;
// You can have more than one arguments, like this:
printf ( " %s world! \n Our integer is %d. \n Our character is '%c'. \n Cyaa world! \n " , c , a , b ) ;
// Never do this:
char * message = " Heyo world! " ;
printf ( message ) ;
// Instead:
char * message = " Heyo world! " ;
printf ( " %s " , message ) ;
// Or:
printf ( " Heyo world! " ) ;
@
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
2023-11-26 15:11:45 -05:00
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
' 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
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 .
2023-11-13 21:03:29 -05:00
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
' 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
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
of reading stuff , try to implement them yourself , it ' s the best approach to learning anything .
2023-11-26 15:11:45 -05:00
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
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 .
2023-11-13 21:03:29 -05:00
*/
extern void print ( char * format , . . . ) ; // Notice the "...", which means it can accept any amount of arguments after that 'format'.
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.
extern void string_print ( char * string , char * format , . . . ) ; // Same as above again, but it'll copy it to some string.
# endif