avocado

How to write your printf in C

A guide to writing printf functions in C from scratch

Welcome everyone, today we will be discussing the printf project.

I would like to emphasize the importance of carefully following the steps outlined in the project, and making sure that you understand each step before moving on to the next one. It's also a good idea to communicate with your partner frequently to ensure that you are both on the same page and that you're progressing smoothly.


Before starting the project, make sure to have a good understanding of the following topics:


What conversion specifier is expected of you to handle?

%cprint a single character
%sprint a string of characters
%%print a percent sign
%dprint a decimal (base 10) number
%iprint an integer in base 10
%bprint a binary number
%uprint unsigned integer
%oprint a number in base 8
%xprint a hexadecimal in lowercase
%Xprint a hexadecimal in uppercase
%Sprint a string with hex values of non-visible characters
%rprints a reversed string
%Rprints the Rot13 interpretation of a string

What flag characters are expected of you to handle?

+the plus sign (+) forces the output to include a sign (+ or -)
``similar to the plus sign flag, the space flag inserts a space character before positive numbers instead of a plus sign
#used to prefix the output with "0x" for hexadecimal numbers, "0" for octal number
l and hmodify the size of the argument passed to the conversion specifier
widthspecifies the minimum number of characters to be printed
precision (.)specifies the precision of the output. For example, %.2d
0specifies that the output should have a minimum width of 6 digits, with leading zeros if necessary
-used to left-justify the output within the specified width

Example for each conversion specifier.

  • Task 0
    _printf("Let's print a simple sentence."); // Let's print a simple sentence.
    _printf("%s", "I am a string !"); // I am a string !
    _printf("%c", 'S'); // S
    _printf("%%"); // %
  • Task 1
    _printf("%d", 1024); // 1024
    _printf("%d", -1024); // -1024
    _printf("%d", INT_MAX); // 2147483647
  • Task 2
    _printf("%b", 98); // 1100010
  • Task 3
    _printf("%u", INT_MAX + 1024); // 2147484671
    _printf("%o", INT_MAX + 1024); // 20000001777
    _printf("%x", INT_MAX + 1024); // 800003ff
    _printf("%X", INT_MAX + 1024); // 800003FF
  • Task 4 check if you're using a local buffer, in this way, we can minimize the number of calls to the write function and improve the performance of the _printf function.
  • Task 5
    _printf("%S\n", "Best\nSchool"); // Best\x0ASchool
  • Task 6
    void *addr = (void *)0x7ffe637541f0;
    _printf("%p", addr); // 0x7ffe637541f0
  • Task 7
    _printf("%+d", 1024); // +1024
    _printf("%+d", -1024); // -1024
    _printf("%+d", 0); // +0
  • Task 8
    _printf("%ld", 1024L); // 1024
    _printf("%ld", -1024L); // -1024
  • Task 9
    _printf("%6d", 102498402); // 102498402
    _printf("%6d", 1024); // __1024 (symbol _ means space)
  • Task 10
    _printf("%.6d", 1024); // 001024
  • Task 11
    _printf("%06d", -102498402); // -102498402
    _printf("%06d", 1024); // 001024
  • Task 12
    _printf("%-6d", 1024); // 1024__
  • Task 13
    _printf("%r", "\nThis sentence is retrieved from va_args!");
    // !sgra_av morf deveirter si ecnetnes sihT
  • Task 14
    printf("%R", "Guvf fragrapr vf ergevrirq sebz in_netf!\n");
    // This sentence is regraded from va_args
  • Task 15 Checks for all the above options work together

Example of what your custom printf function might look like.

#include <stdarg.h>  // For handling variadic arguments
#include <stdio.h>   // For standard input/output
 
int print_integer(int value);
 
void print_buffer(char buffer[], int *buff_ind);
 
#define BUFF_SIZE 1024
 
int _printf(const char *format, ...)
{
	va_list args;      // Declare a variable to hold the list of arguments
	int count = 0;     // Initialize a counter for the number of characters printed
	int printed = 0;   // Number of characters printed by each conversion specifier
	int buff_ind = 0;  // Buffer index
	char buffer[BUFF_SIZE];
 
	va_start(args, format); // Initialize the argument list
 
	while (*format) // Iterate over each character in the format string
	{
		if (*format == '%') // Check for the start of a conversion specifier
		{
			format++; // Move to the next character after '%'
 
			if (*format == '%') // Case: '%%' prints a single '%'
			{
				buffer[buff_ind++] = '%';
				if (buff_ind == BUFF_SIZE)
				{
					print_buffer(buffer, &buff_ind);
					count += buff_ind;
				}
			} else if (*format == 'c') // Case: '%c' prints a character
			{
				int ch = va_arg(args, int); // Fetch the next argument as int
				buffer[buff_ind++] = ch;
				if (buff_ind == BUFF_SIZE)
				{
					print_buffer(buffer, &buff_ind);
					count += buff_ind;
				}
			} else if (*format == 's') // Case: '%s' prints a string
			{
				char *str = va_arg(args, char *); // Fetch the next argument as char*
				while (*str) // Iterate over each character in the string
				{
					buffer[buff_ind++] = *str;
					str++;
					if (buff_ind == BUFF_SIZE)
					{
						print_buffer(buffer, &buff_ind);
						count += buff_ind;
					}
				}
			} else if (*format == 'd' ||
			           *format == 'i') // Case: '%d' or '%i' prints an integer
			{
				int value = va_arg(args, int);
				printed = print_integer(value);//calling print_int function
				count += printed;
			}
		} else // Case: Regular character, not a conversion specifier
		{
			buffer[buff_ind++] = *format;
			if (buff_ind == BUFF_SIZE)
			{
				print_buffer(buffer, &buff_ind);
				count += buff_ind;
			}
		}
 
		format++; // Move to the next character in the format string
	}
 
	print_buffer(buffer, &buff_ind);
	count += buff_ind;
 
	va_end(args); // Clean up the argument list
 
	return count; // Return the number of characters printed
}
 
int print_integer(int value)
{
	char buffer[32]; // Buffer to store the string representation
	int printed = 0; // Counter for the number of characters printed
 
	// Handle the case of negative numbers
	if (value < 0)
	{
		putchar('-');
		printed++;
		value = -value;
	}
 
	// Convert each digit of the integer to a character
	int i = 0;
	do
	{
		buffer[i++] = '0' + (value % 10);
		value /= 10;
		printed++;
	} while (value > 0);
 
	// Print the characters in reverse order to get the correct representation
	for (int j = i - 1; j >= 0; j--)
	{
		putchar(buffer[j]);
	}
 
	return printed;
}
 
void print_buffer(char buffer[], int *buff_ind)
{
	if (*buff_ind > 0)
	{
		for (int i = 0; i < *buff_ind; i++)
		{
			putchar(buffer[i]);
		}
	}
 
	*buff_ind = 0;
}
 
// For testing the code
#include <stdio.h>
 
int main()
{
	int num = 123;
	char character = 'A';
	char string[] = "Hello, World!";
 
	printf("Printing an integer: %d\n", num);
	printf("Printing a character: %c\n", character);
	printf("Printing a string: %s\n", string);
 
	return 0;
}

Compile your code with:

$ gcc -Wall -Werror -Wextra -pedantic -std=gnu89 *.c

After completing the implementation, how would you submit your project?

  1. One of the partners should create a new repository on GitHub.

  2. open the collaborators settings, add the partner's username or email.

  3. The two partners alternately pushes one file after the other.

  4. It's important that the commits made by partners are equal, or have a difference of 60% to 40%. Otherwise, you will receive a score of 0.

    ⇒ When you check the repository insights, you should see that you are equal. Untitled


Hints

  1. Each partner tries to work on the project separately, and once they finish, they will compare their work to see who completed the project better with fewer errors. Then, they will work together to improve their work and divide the files to push them on GitHub.
  2. One partner will start the implementation for the first two tasks, which form the structure for the rest of the project. After that, they will take turns solving the remaining tasks. Communication is crucial for success.
  3. In case your partner is not responding or not cooperating with you to complete the project, don't worry. You can always reach out to your mentor for help. They can assist you in changing your partner if needed.