Project 4: printf Function

Printf:
http://www.cplusplus.com/reference/cstdio/printf/
https://linux.die.net/man/3/snprintf
http://man7.org/linux/man-pages/man3/printf.3.html
https://www.cypress.com/file/54441/download

man 3 printf
int printf(const char * restrict format, …);
This function writes to the standard output stream. The output uses a specific format in accordance with strdarg(3).

The restrict keyword with the pointer indicates that the ptr is the only way to access the object pointed to it so the compiler doesn’t need to perform any additional checks (optimization).
https://www.geeksforgeeks.org/restrict-keyword-c/

An optional field, consisting of a decimal digit string followed by a
$, specifying the next argument to access. If this field is not pro-
vided, the argument following the last argument accessed will be
used. Arguments are numbered starting at 1. If unaccessed arguments
in the format string are interspersed with ones that are accessed the
results will be indeterminate.

int main(void)
{ printf(“Hello %2$d %d %d %3$s %s”, 25, 32, “Hello”, 27); }

Hello 32 25 32 Hello Hello

In this case, the first pointer is accessing the 2nd element, which is of decimal type, hence why the 32 is printed out. When we do %d and %d twice, the 2nd pointer start again at the first argument because it is a new pointer, hence why you get 25 42. When we do %3s, the first pointer is then accessing the third element which is a string. When we do %s, the second pointer continues incrementing and we are now at “Hello.”

int main(void)
{ printf(“Hello %1$f %f %d”, 2.54, 32); }

Hello 2.540000 2.540000 32

In this case, the first pointer points to the first argument which is a float. The 2nd pointer is for %f %d and then points to 2.54 and increment and points to 32.

Overall Explanation: The reason that this works is because the $ and without the $ both have separate pointers. If you were to try to access a string when there is no string as the next argument, then you might get a segmentation fault or an error, like below:
int main(void) { printf(“Hello %1$f %f %s”, 2.54, 32); }
This will not work because 32 is a digit/integer rather than a string.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
%[flags][min field width][precision][length]conversion specifier
The terminology may be a little messy throughout the document, but follow this website for the correct usage of the above words: http://www.cplusplus.com/reference/cstdio/printf/

  • Conversion Specifiers: a character that specifies the type of conversion to be applied. (d, i, o, u, x, X, f, c, s, p)
  • Length Modifiers: formatting for integers (diouxX)
    * hh, h, l, ll
    * L
    (f)
  • Flag characters: the character % is followed by zero or more of the following flags: #, 0, -, ‘ ‘, +

Conversions: csp (aka “format identifiers”)
c: character
s: string
p: pointer

Conversions with flags: diouxX & hh, h, l, ll
d & i: decimal signed integer
o: octal integer
u: unsigned integer
x: hex integer {0123456789abcdef}
X: hex integer (capital version), {0123456789ABCDEF}

Length:
hh: signed char or unsigned char (1 byte)
h: short int or unsigned short int (2 byte)
l: long int or unsigned lont int (8 byte) → at least 4 bytes → eg. 65000L
ll: long long int or unsigned long long int (8 byte) → eg. 65000LL

This reads bytes from right to left. A long time ago, we talked about little endian and big endian and how MAC computers at 42 use little endian, aka it starts counting the bytes from right to left in this case (but look below for what actually happens). So, in the case of hh…
If we have a decimal number of 578, indicated
0b0000 0010 0100 0010
The 0100 0010 is read because only 1 byte is read. (right side)

As a reminder, endianness is used to describe the order in which a sequence of bytes are stored in computer memory. Big vs little endian describes which value is stored first. Big endian means the largest or most significant value is stored first at the lowest storage address. In a little endian system, it’s the least significant value is stored first. Macs use little endianness. So, a 4F52 number would technically be read as 524F, which means 42 stored at address 1000, and 4F stored at address 1001.

We typically read via big endian aka as 4F52, but an argument for using little endian is that as the number grows, you can just add more digits to the right at a higher address instead of shifting everything.

Conversions: f with flags l & L
f: float (3.14159)

printf ("Decimals: %d %ld\n", 1977, 650000L);

"%f" is the (or at least one) correct format for a double. There is no format for a float, because if you attempt to pass a float to printf, it'll be promoted to double before printf receives it1. "%lf" is also acceptable under the current standard -- the l is specified as having no effect if followed by the f conversion specifier (among others). (Source)

printf(“\n%lf”, (double)32.3);

Flags:
l: long or unsigned long

l (ell) Specifies that (…) has no effect on a following a, A, e, E, f, F, g, or G conversion specifier. The l modifier is required in scanf with double, but not in printf . because it is automatically promoted by va_args (Source). Therefore, %lf and %f are equivalent.

L: long double (f)

Manage: %%, #0-+ and space

  • %%: the % indicates a format escape sequence used for formatting the variables. In other words, this is how to write 20%:

int main(void) {
printf("20%%");
return 0; }

  • #: various uses
    - #o: 0 prefix inserted
    - #x: 0x prefix added to non-zero values
    - #X: 0X prefix added to non-zero values
    - #f: always show the decimal point

printf(“%#x”, 10); → 0xa

float f = 12;
printf(“%#.0f”, f); → 12. (The .0f makes it 12 & the # adds the .)

  • 0: padded on the left with 0’s instead of blanks

printf(“Test : %05d”, 10); → 00010

  • - : left justifies

printf(“%-10s”, “Hello”);

  • +: sign of a number. By default, a ‘-’ sign is showed for negative numbers.

printf(“%+d”, 10);

Minimum Field-Width:
An optional digit string with nonzero first digit.

printf("|%5d|", 5); → |’’’’5| The’ indicates spaces. 5 characters in the field

If there are fewer characters than the field with, it will be padded on the left (or right if used with left justify flag).

printf(“|%-5d|”, 5); → |5'’’’| The’ indicates spaces. 5 characters in the field

You can also used * or *m$ (for some decimal integer m) to specify the field width is in the next (*) or (m) argument. This has to be an integer.

printf(“|%*d|”, 5, 1); → |’’’’1| The next arg 5 indicates the spacing. 1 indicates what will be printed

printf(“%d %d |%*3$d|”, 5, 1, 10); → 5 1 |’’’’’’’’10|. 8 spaces in the field.

Precision:
In the form of a period (.) followed by a decimal. This allows us to limit the size/length of a number. You can use * or *m$, similar to minimum field width.

If the precision is just ‘.’ then the precision will be 0.

printf(“%.5d”, 20); → 00020
printf(“%.d”, 20); → 20

printf(“%.3f”, 20.0); → 20.000
printf(“%.f”, 20.0); → 20

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

man 3 stdarg
https://www.youtube.com/watch?v=-dZpDPFp6YU

Variable argument list, aka a function can take in multiple arguments
#include <stdarg.h>

va_list object is created. Used by the macros below

  • void va_start(va_list ap, last);
    Must be called first.
    Initializes ap.
    last is the variable passed into the function before the ellipses
  • type va_arg(va_list ap, type);
    Keeps track of the last argument value returned. (Probably a pointer. Increment so it returns the next appropriate argument value in the ap list).
  • void va_copy(va_list dest, va_list src);
    Copies src to dstand preserves the state in dest(similar to calling va_start and va_arg for src).
  • void va_end(va_list ap);
    Calling this indicates no further arguments. Ends the va_start.

int function(int num, …){

int count = 0;
va_list argptr;

va_start(argptr, num);

while(count < num)
{ sum+= va_arg(argptr, int);
count++;}
va_end(argptr);
return sum;
}

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Unions:
https://www.youtube.com/watch?v=T976YpsV62w&t=334s
This is a user-defined data structure in which multiple variables are sharing the same memory location. This is a way to save memory.

Struct vs Union:
https://www.geeksforgeeks.org/difference-structure-union-c/
For struct, all elements get independent memory locations. For union, all elements get the same memory location

Enums:
Enum is a collection of constant that can be used for more readable code. The default first enum member is “0” unless you assign it.

typedef enum Weekday
{
Sun=1,Mon,Tues,Wed,Thrs, Fri, Sat
} WEEKDAY;

WEEKDAY wd;
wd = Sun;

printf(“%d”, wd);

Possible additional feature:
https://blog.alicegoldfuss.com/function-dispatch-tables/
https://github.com/MrColour/ft_printf/blob/4171d6143cd4fb7d54598c46b1cdde7fee7de708/minimain.c
https://github.com/MrColour/ft_printf/blob/4171d6143cd4fb7d54598c46b1cdde7fee7de708/includes/color.h

Quick way to test code:
int mine_result;
int syst_result;
#define VERIFY(msg, …) mine_result = ft_printf(“[MINE]: |”msg”|\n”, ## __VA_ARGS__); \
syst_result = printf(“[SYST]: |”msg”|\n”, ## __VA_ARGS__); \
if (mine_result == syst_result) \
printf(“[RESULT]: [MINE]: %d [SYST]: %d\n””\n”, mine_result, syst_result); \
else \
printf(“[RESULT]: [MINE]: %d [SYST]: %d\n””\n”, mine_result, syst_result);

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Standard Integer Types
<stdint.h> Standard Integer Types
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html

It is recommended to use the standard integral types rather than “long long’ and “long int” words because “long long” and “long int” can vary according to the compiler. A long int is guaranteed to be at least 32 bits. A long long int is guaranteed to be at least 64 bits.

When we do “int32_tand int64_t, it is more transparent to see how many bits that we are allocating/looking at.

VA_ARG Type
va_arg converts anything smaller than an integer into an integer. As a result, a short int and a char becomes an int.

va_arg converts a float into a double.
https://en.wikipedia.org/wiki/Stdarg.h

You could see the error on repl when you try to use va_arg(argptr, char). The error message you will get is:
warning: second argument to ‘va_arg’ is of promotable type ‘char’; this va_arg has undefined behavior because arguments will be promoted to ‘int’ [-Wvarargs]

To get rid of this error, what you could do is cast it back to a char, such as
printf(“%c”, (char)va_arg(argptr, int));

Groupings for va_arg (d, i, o, u, x, X, f, c, s, p)
d, i, o, u, x, X, c
f
s
p

Printing address using int64_t
Pointers are stored as signed integers. You can convert this integer into a hexadecimal value to show the location or address to therefore print it out.

How to push to two repositories
https://stackoverflow.com/questions/14290113/git-pushing-code-to-two-remotes

--

--