0
votes

I am trying to create a simplified version of printf() but without the use of va_list, va_start, va_arg and va_end.

My original idea is to have:

void my_printf (char *format, ...);

Then, go through the format, and count the number of % (arguments) to find out how many variables were passed to my function. From there, I was thinking of creating a buffer based on how many arguments there are, then combine then and use write() to finally output them.

Is there a better way of approaching this problem? Will I run into issues with my plan?

Any help would be greatly appreciated.

Some of my brainstorming code is:

// Count arguments
int cnt_s, cnt_c, cnt_d, cnt_u, cnt_x;
for (; *format; format++) {
    switch(format[0]) {
        case '%':
            switch(format[1]) {
                case 'd':
                    cnt_d++;
            }
    }
}
2
You need va_list, va_start, va_arg, and va_end to access the arguments specified by the ... - pmg
I was thinking of accessing them directly from memory. What would I use besides ... then? - Pat
That's a hilariously bad idea. Why are you trying to circumvent the standard means provided for accessing variable length argument arrays? - meagar♦
Its not for any practical applications. - Pat

2 Answers

4
votes

You need va_list, va_start, va_arg, and va_end to access the arguments specified by the ...

Unless you want to use void* and unions ----- UGH!

3
votes

The best way I can think of to do this (NOTE: this is potentially unsafe) would be to take the address of the format string and increment from that to find the arguments.

Heres a working example I hacked together (assumes implementation of other print functions)

int my_printf(char *format, ...) {
    void *beg = &format;
    int i;
    beg += sizeof(char*);
    for (i = 0; i < strlen(format); i++) {
        if (format[i] == '%') {
            switch (format[i+1]) {
                case 's':
                    print_s(*((char**)beg)); 
                    beg += sizeof(char*);
                    break;
                case 'd':
                    print_d(*((int*)beg)); 
                    beg += sizeof(int);
                    break;
                case 'f':
                    print_f(*((float*)beg)); 
                    beg += sizeof(float);
                    break;
            }
            i++;
        } else {
            putchar(format[i]);
        }
    }
}

This could potentially cause some unintended side effects if you dont provide enough arguments, and it might require some tweaking. But thats the idea.

Explanation: This works because all function arguments are shoved on the stack in order. So since you have the first argument (the format string) you can take its address and increment according to the desired argument to directly access the arguments on the stack without any VA_ARGS magic.