printf
This project aims at rebuilding the printf library using limited function like write and variadic function. This post is not yet finished.
Printf
Variadic function
va_start
va_start takes two arguments, a va_list object and a reference to the function’s last parameter (the one before the ellipsis; the macro uses this to get its bearings). It initialises the va_list object for use by va_arg or va_copy.
va_arg
va_arg’ takes two arguments, a ‘va_list’ object previous initialised and a type descriptor. It expands to the next variable argument, and has the specified type. Successive invocations of ‘va_arg’ allow processing each of the variable arguments in turn.
va_end
‘va_end’ takes one argument, a ‘va_list’ object. It serves to clean up.
va_copy
‘va_copy’ takes two arguments, both of them ‘va_list’ objects. It clones the second (must be initialised) into the first.
Format
‘diouxX’: The int (or appropriate variant) argument is converted to signed decimal (d and i), unsigned octal (o), unsigned decimal (u), or unsigned hexadecimal (x and X) notation. The letters ``abcdef’’ are used for x conversions; the letters ABCDEF are used for X conversions. The precision, if any, gives the minimum number of digits that must appear; if the converted value requires fewer digits, it is padded on the left with zeros.
‘c’: The first byte of argument is printed.
’s’: Bytes from the string argument are printed until the end is reached or until the number of bytes indicated by the precision specification is reached; however if the precision is 0 or missing, the string is printed entirely.
‘p’: The void * pointer argument is printed in hexadecimal.
Flag
‘0’: Zero padding. The converted value is padded on the left with zeros rather than blanks. If a precision is given with a numeric conversion, the 0 flag is ignored.
’-‘: A negative field width flag; the converted value is to be left adjusted on the field boundary. The converted value is padded on the right side with blanks, rather than on the left with blanks or zeros. A ‘-‘ overrides a ‘0’ if both are given.
’’’: Decimal conversions(d, u or i) or the integral portion of a floating point conversion(f or F) should be grouped and separated by thousands using non-monetary separator returned by localeconv.
field Width
An optional digit string specifying a field width; if the output string has fewer bytes than the field width it will be blank-padded on the left to make up the field width(a leading zero is a flag, but an embedded zero is part of a field width).
Precision
An optional period, ‘.’, followed by an optional digit string giving a precision which specifies the number of digits to appear after the decimal point, for ‘e’ and ‘f’ formats, or the maximum number of bytes to be printed from a string; if the digit string is missing, the precision is treated aszero.
Pointer and const modifier
const always modifies the thing that comes before it (to the left of it), EXCEPT when it’s the first thing in a type declaration, where it modifies the thing that comes after it (to the right of it).
int * mutable_pointer_to_mutable_int;
int const * mutable_pointer_to_constant_int;
int *const constant_pointer_to_mutable_int;
int const *const constant_pointer_to_constant_int;
Stack and Heap
It is wrong to return a reference to a local variable, because the memory for this variable is deallocated when the function ends. The following example shows the problem :
/* wrong version */
int *table_of(int num, int len)
{
int table[len+1];
for (int i=0; i <= len; i++)
{
table[i] = i*num;
}
return table;
}
/* A cleaner solution would be to let the caller
allocate the momory and pass in a pointer to that */
void table_of(int num, int len, int[] table);
Stack
Data on the stack can be used without pointers.
The limitation of stack-allocated memory is that we need to know the size of that table needed beforehand.
Heap
Data on the stack is allocated automatically when we do a function call, and removed when we return.
Data on the heap must be allocated and de-allocated manually, using malloc and free.
malloc
The operation to allocate a chunk of memory on the heap is malloc.
void*
void* is the type of untyped pointers. A void* pointer can be converted into any other pointer type without an explicit cast.
free
Never free memory that is not dynamically allocated.
/* "hello" is statically allocated */
char *x = "hello";
free(x);
aliasing
Pointers are called aliases if they both point to same address. Aliasing can make some of the bugs hard to spot.
int *x = malloc(1000);
int *y = malloc(1000);
int *z = x;
y = x;
int *w = y;
/* double free ! */
free(w);
free(z);
Pointer
What is the basic function of pointer ?
Swap value. In C, variables is passed as a copy into argument when calling functions. The modification we make will not influence the origin. However, pointer stores memory address of a variable. If we change a value at a certain memory address, it will be always there.
void swap_wrong(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
void swap_true(int *x, int *y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
Linked List
Why do we use pointer to linked list element when declaring it ?
-
Because we use pointer to get reference of the next node. A pointer to a node will simplify the linking step. The more important is that, manipulating the pointer to a variable declared locally is not decent.
-
By allocating memory for node in heap, the lists will remain as long as we wish. If we allocates it in stack by the traditional way like that for int, the lists will be destroyed when function ends. What’s more, the space in stack in much less than that in heap.
Why sometimes double pointers ?
In order to amend a variable in a calling function from within a called function, you need to pass a pointer to the variable as a function argument. This provides the called function with the memory address of the variable to be amended. Dereferencing this (pointer) variable within the called function provides access to the original value.
#include <stdlib.h>
typedef struct s_linked_list
{
int data;
struct s_linked_list *next;
} t_linked_list;
int main(void)
{
t_linked_list *head = NULL;
t_linked_list *second = NULL;
t_linked_list *end = NULL;
head = (t_linked_list *)malloc(sizeof(t_linked_list));
second = (t_linked_list *)malloc(sizeof(t_linked_list));
end = (t_linked_list *)malloc(sizeof(t_linked_list));
head->data = 1;
head->next = second;
second->data = 2;
second->next = third;
end->data = 3;
third->next = NULL;
}