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?
%c | print a single character |
|---|---|
%s | print a string of characters |
%% | print a percent sign |
%d | print a decimal (base 10) number |
%i | print an integer in base 10 |
%b | print a binary number |
%u | print unsigned integer |
%o | print a number in base 8 |
%x | print a hexadecimal in lowercase |
%X | print a hexadecimal in uppercase |
%S | print a string with hex values of non-visible characters |
%r | prints a reversed string |
%R | prints 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 h | modify the size of the argument passed to the conversion specifier |
width | specifies the minimum number of characters to be printed |
precision (.) | specifies the precision of the output. For example, %.2d |
0 | specifies 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 *.cAfter completing the implementation, how would you submit your project?
-
One of the partners should create a new repository on GitHub.
-
open the collaborators settings, add the partner's username or email.
-
The two partners alternately pushes one file after the other.
-
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.

Hints
- 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.
- 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.
- 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.