Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-

stdarg.h in C

Name: Anonymous 2012-09-05 16:00

I'm currently writing a C standard library for academic purposes, and I wondered how to implement stdarg.h (the va_* macros) in C (probably with inline ASM) and if this was the libc's work, or the compiler's ?
Thanks in advance.

Name: Anonymous 2012-09-05 16:12

Varargs are part of the C language syntax, not the standard library. If you're implementing them in a header, you're doing it wrong.

Name: Anonymous 2012-09-06 2:11

>>1
It depends on the C ABI of the target platform, and how arguments are passed in memory on the stack to a function.

The va_* macros are usually implemented in terms of pointer arithmetic... simply incrementing/decrementing to the next machine-word sized location on the stack.

Name: Anonymous 2012-09-06 2:18

Sometime ago I wrote a simple OS and it just used macros from stdarg.h

so you never need to touch them.


#define VIDEO_WIDTH 80
#define VIDEO_HEIGHT 25
#define VIDEO_RAM 0xb8000
#include <stdarg.h>
#include "tty.h"

int tty_cursor;
int tty_attribute;

void init_tty() {
    tty_cursor = 0;
    tty_attribute = 10;
}

static int textcolor(int c) {
    tty_attribute = c;
}

void clear_screen() {
    char *video = (char*) VIDEO_RAM;
    int i;
    for (i = 0; i < VIDEO_HEIGHT*VIDEO_WIDTH; i++) {
        *(video + i*2) = ' ';
    }
    tty_cursor = 0;
}

void putchar(char c) {
    char *video = (char*) VIDEO_RAM;
    int i;
    switch (c) {
    case '\n':
        tty_cursor+=VIDEO_WIDTH;
        tty_cursor-=tty_cursor%VIDEO_WIDTH;
        break;
    default:
        *(video + tty_cursor*2) = c;
        *(video + tty_cursor*2+1) = tty_attribute;
        tty_cursor++;
        break;
    }
    // do we need to shift screen horizontally?
    if (tty_cursor>VIDEO_WIDTH*VIDEO_HEIGHT) {
        for (i=VIDEO_WIDTH*2;i<=VIDEO_WIDTH*VIDEO_HEIGHT*2+VIDEO_WIDTH*2;i++) {
            *(video+i-VIDEO_WIDTH*2)=*(video+i);
        }
        tty_cursor-=VIDEO_WIDTH;
    }
}

static void reverse(char *s) {
    char *p = s;
    while(*p) p++;
    while(s < --p) {
        int t = *s;
        *s++ = *p;
        *p = t;
    }
}


static char *puthex_digit(char *b, unsigned char digit) {
    char table[]="0123456789ABCDEF";
    *b++ = table[digit];
    return b;
}

static char *puthex_tetra(char *b, unsigned char byte) {
    b = puthex_digit(b, byte >> 4);
    b = puthex_digit(b, byte & 0x0F);
    return b;
}

static char *puthex(char *b, unsigned int dword) {
    b = puthex_tetra(b, (dword & 0xFF000000) >>24);
    b = puthex_tetra(b, (dword & 0x00FF0000) >>16);
    b = puthex_tetra(b, (dword & 0x0000FF00) >>8);
    b = puthex_tetra(b, (dword & 0x000000FF));
    return b;
}

static char *int2string(char *b, int v, int base, int unsign) {
    char *p = b;
    unsigned int x;
    int sign = 0;

    if(v < 0 && !unsign) {
        sign = 1;
        x = -v;
        *p++ = '-';
    } else {
        if(v < 0) x = -v;
        else x = v;
    }

    do {
        unsigned int c = x % base;
        x /= base;
        if(c < 10) c += '0';
        else {
            c -= 10;
            c += 'A';
        }
        *p++ = c;
    } while(x);
    *p = 0;
    reverse(b+sign);
    return p;
}

static void vsprintf_helper(char *b, const char *fmt, va_list args) {
    for( ; *fmt; fmt++) {
        if(*fmt != '%') {
            putchar(*fmt);
            continue;
        }
        fmt++;
        switch (*fmt) {
        case 's': {
            char *p = va_arg(args, char *);
            while(*b++ = *p++);
            b--;
            break;}

        case 'c':
            *b++ = va_arg(args, unsigned int);
            break;

        case 'i': case 'd':
            b = int2string(b, va_arg(args, unsigned int), 10, 0);
            break;
   
        case 'u':
            b = int2string(b, va_arg(args, unsigned int), 10, 1);

        case 'x': case 'X':
            b = puthex(b, va_arg(args, unsigned int));
            break;

        case 'z':
            textcolor(va_arg(args,unsigned int));
            break;
        }
    }
    *b = 0;
}

void puts(const char *s) {
    while(*s) putchar(*s++);
}


void vsprintf(char *b, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsprintf_helper(b, fmt, args);
    va_end(args);
}

void printf(const char *fmt, ...) {
    char b[512];
    va_list args;
    va_start(args, fmt);

    textcolor(0xf);

    vsprintf_helper(b, fmt, args);

    va_end(args);
    puts(b);
}

Name: Cudder !MhMRSATORI!fR8duoqGZdD/iE5 2012-09-06 4:05

reverse(b+sign);
Writing out the digits in reverse... and then reversing them again? Seriously?

Name: Anonymous 2012-09-06 8:25

>>4
neat. What type of device did this run on?

Name: Anonymous 2012-09-06 8:33

>>6
#define VIDEO_RAM 0xb8000

Name: Anonymous 2012-09-06 10:37

#defineMY ANUS

Name: Anonymous 2012-09-07 1:57

>>7
U mena IBM PC Compatible?

Name: Anonymous 2012-09-07 2:18

>>5
That's my favorite part!  Clever reuse of the output buffer...

Name: Cudder !MhMRSATORI!fR8duoqGZdD/iE5 2012-09-07 3:38

>>10
Unnecessary memory accesses.

Find out how long the number will be, and fill the buffer backwards from there. Simple and efficient.

It's more likely he did that because "reverse a string in place" is one of those "clever interview questions" that have very little use in practice and he just wanted to show it off.

Name: Anonymous 2012-09-07 4:13

Variadic functions complicate type checking and optimisation. Abandon and replace them with more efficient meta-programming-based techniques.
For instance, you can have a preprocessor turn printf("%02x: %s\n", i, strings[i]); into print_hex(2, i); print_string(": "); print_string(strings[i]);.

Name: 12 2012-09-07 4:14

I forgot the newline but you get the idea.

Name: Cudder !MhMRSATORI!fR8duoqGZdD/iE5 2012-09-07 4:20

>>12
I doubt that is more efficient especially as the format string becomes more complicated. 5 bytes per call, not including all the extra parameters passed.

Name: Anonymous 2012-09-07 11:28

So, the compiler takes care of this ?
Great then, thank everybody.

Also, this will be for x86-64 only.

Name: Anonymous 2012-09-07 13:03

>>12
I'm not convinced that is even true in general, but at least for printf the time it takes to create the printable string from the format and parameters is trivial compared to the time it takes to output the buffer. So even if you changed it to be only one syscall as in

  char line[LINE_LEN];
  sprintf(line, "%2x", i);  // your specific hex-to-string function here
  strncpy(line + 2, ": ", 2);
  strcpy(line + 4, strings[i]);
  puts(line);


you wouldn't gain much. When is printf a bottle-neck anyway? Could you as easily chop another variadic function into separate functions?

Name: Anonymous 2012-09-07 13:59

>>16
>strncpy(line + 2, ...)
I think you mean "strcat".

Name: Anonymous 2012-09-07 14:41

Could-er, what is the best way for a human with little to no knowledge of programming to learn programming?

Before you say that, that must be implementation specific, consider the case of the average person.

Name: Anonymous 2012-09-07 14:42

>>17
Actually he means memcpy.

Name: Anonymous 2012-09-07 15:04

ACTUALLY HE MENA HASKALL

Name: Anonymous 2012-09-07 15:15

MENA MY ANUS

Name: Anonymous 2012-09-07 17:30

>>16
Replace printf with sprintf in an innermost loop and it may very well become a bottleneck.

Name: Anonymous 2012-09-09 15:14

>>19
Oh, that's right. Will correct >>16.

*edit: how do I edit?

Don't change these.
Name: Email:
Entire Thread Thread List