Selaa lähdekoodia

More about printf function...

master^2
Ognjen Milan Robovic 6 kuukautta sitten
vanhempi
commit
1c3cc4c779
3 muutettua tiedostoa jossa 125 lisäystä ja 32 poistoa
  1. +53
    -28
      chapters/chapter_3.c
  2. +71
    -3
      chapters/chapter_3.h
  3. +1
    -1
      xhartae.c

+ 53
- 28
chapters/chapter_3.c Näytä tiedosto

@@ -11,55 +11,80 @@ It is distributed in the hope that it will be useful or harmful, it really depen

#include "chapter_3.h"

static void print_colour (char colour_id) {
switch (colour_id) {
case '/': out ("/", 1); break;
case '0': terminal_colour (COLOUR_GREY, EFFECT_BOLD); break;
case '1': terminal_colour (COLOUR_RED, EFFECT_BOLD); break;
case '2': terminal_colour (COLOUR_GREEN, EFFECT_BOLD); break;
case '3': terminal_colour (COLOUR_YELLOW, EFFECT_BOLD); break;
case '4': terminal_colour (COLOUR_BLUE, EFFECT_BOLD); break;
case '5': terminal_colour (COLOUR_PINK, EFFECT_BOLD); break;
case '6': terminal_colour (COLOUR_CYAN, EFFECT_BOLD); break;
case '7': terminal_colour (COLOUR_WHITE, EFFECT_BOLD); break;
case '-': terminal_colour (COLOUR_WHITE, EFFECT_NORMAL); break;
default: terminal_colour (COLOUR_WHITE, EFFECT_NORMAL); break;
}
}

static void print_format (char format_id, va_list argument_list) {
switch (format_id) {
case '%': {
out ("%", 1);
} break;
case 'i': {
int integer;
integer = va_arg (argument_list, int);
echo (number_to_string (integer));
} break;
case 'f': {
double ieee754;
ieee754 = va_arg (argument_list, double);
echo (number_to_string ((int) ieee754));
} break;
case 's': {
char * string;
string = va_arg (argument_list, char *);
echo (string);
} break;
default: {
out ("?", 1);
} break;
}
}

void print (char * format, ...) {
va_list argument_array;
va_list argument_list;

int offset, length;

int integer;
char * string;
double ieee754;

length = string_length (format);

va_start (argument_array, format);
va_start (argument_list, format);

for (offset = 0; offset != length; ++offset) {
if (format [offset] == '%') {
if (format [offset] == '/') {
++offset;
if (format [offset] == '%') {
out ("%", 1);
} else if (format [offset] == 'i') {
integer = va_arg (argument_array, int);
echo (number_to_string (integer));
} else if (format [offset] == 'F') {
ieee754 = va_arg (argument_array, double);
echo (number_to_string ((int) ieee754));
} else if (format [offset] == 's') {
string = va_arg (argument_array, char *);
echo (string);
} else {
out ("?", 1);
}
} else if (format [offset] == '/') {
print_colour (format [offset]);
} else if (format [offset] == '%') {
++offset;
if (format [offset] == '/') {
out ("/", 1);
}
print_format (format [offset], argument_list);
} else {
out (& format [offset], 1);
}
}

va_end (argument_array);
va_end (argument_list);
}

void file_print (char * format, ...) {
void file_print (int file, char * format, ...) {
(void) file;
(void) format;
return;
}

void string_print (char * format, ...) {
void string_print (char * string, char * format, ...) {
(void) string;
(void) format;
return;
}


+ 71
- 3
chapters/chapter_3.h Näytä tiedosto

@@ -13,8 +13,76 @@ It is distributed in the hope that it will be useful or harmful, it really depen

#include "chapter_0.h"

extern void print (char * format, ...);
extern void file_print (char * format, ...);
extern void string_print (char * format, ...);
/*
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 = 0X0011AAU; printf ("0X%X\n", f);
size_t g = sizeof (a); printf ("%ul\n", g);
int * h = & a; printf ("\t%p", (void *) h);
printf ("\nCyaa world!\n" );

// You can have more than one arguments, like this:

printf ("%s world!\n Our integer is %d.\n Our character is '%c'.\nCyaa world!\n", c, a, b);

// Never do this:

char * message = "Heyo world!";

printf (message);
@

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
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' and 'char *'
pretty much. 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.

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.
*/

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

+ 1
- 1
xhartae.c Näytä tiedosto

@@ -130,7 +130,7 @@ int main (int argc, char * * argv) {
curses_synchronize ();
}

print ("%F\n%s\n%i\n", 69.1, "Heyo", 420);
print ("/1%f/-\n/2// %s/-\n%i\n", 69.1, "Heyo", 420);

return (EXIT_SUCCESS);
}

Loading…
Peruuta
Tallenna