Linux C создает пользовательскую функцию printf без библиотек заголовков

Я работаю над созданием своего собственного prinf() для загрузчика, над которым я работаю для назначения классов, это означает, что я должен использовать компилятор BCC, и я не могу использовать системные библиотеки, так как они не существуют. У меня есть возможность использовать функцию putc(), разработанную на ассемблере, а также функции библиотеки строк strcmp и т. Д., Чтобы помочь мне по мере необходимости.

Кажется, у меня возникла логическая проблема.

Если я определю это в тестовом файле, скомпилированном в Linux (cc):

int a = 0;
int b = 1;
int i1 = 0;
int i2 = 1;
unsigned long x = 42949672;
printf("\nHI! :%d: :%d: :%d: :%d: :%d: :%d:\n", x,a,b,i1,i2,x);

Я могу тогда бежать./a.out и я получаю HI! :42949672: :0: :1: :0: :1: :42949672: что правильно.

Я создал свою собственную функцию printf, и когда я вижу напечатанные вещи, я вижу HI! :23592: :655: :0: :1: :0: :1:, что не правильно. Я пробовал печатать только с целыми числами, и он отлично работает, но когда я пытаюсь напечатать длинную без знака, у меня возникают проблемы.

Вот мой код:

void prints(s) char *s;
{
        int i;
        for(i=0; i<strlen(s); i++)
                putc(s[i]);
}

void gets(s) char *s;
{
        //LEC9.pdf
        while( (*s=getc()) != '\r')
        {
                putc(*s++);
        }
 *s = '\0';
}

//EXAMPLE CODE
char *ctable = "0123456789ABCDEF";
int rpi(x, BASE) unsigned long x; int BASE;
{
        char c;
        if (x)
 {
                c = ctable[x % BASE];
                rpi(x / BASE, BASE);
                putc(c);
        }
 return 0;
}

void printc(ip) unsigned long;
{
        putc(ip);
}
int printd(ip) unsigned long;
{
        if(ip < 0)
        {
                putc('-');
                ip = -ip;
        }
 if(ip == 0)
 {
  putc('0');
  return 0;
 }
 rpi(ip, 10);
}
void printx(ip) unsigned long;
{
        prints("0x"); //PUT OR OUTPUT LOOK LIKE INT
        rpi(ip, 16);
}
int printl(ip) unsigned long;
{
 if(ip == 0)
 {
  putc('0');
  return 0;
 }
        rpi(ip, 10);
        putc('L');
}
void printf(fmt) char *fmt;
{
        char *cp;               //POINTER TO LOOP THROUGH
        unsigned long *ip;     //POINTER FOR

        cp = fmt;               //SET POINTER TO START POINTER {FMT}
        ip = &fmt+1;            //Board says &fmt:but will not work without +1

        while(*cp)
        {
                //IF C != %
                if(*cp != '%')
                {
                        printf("%c", *cp);
                        if(*cp == '\n')
                        {
                                //putc('\n'); //implied
                                putc('\r');
                        }
                        cp++;
                        continue; //NEXT CHAR
                }
                else
                {
                        //MOVE ONE CHARACTER (%{x}) SO WE CAN GET x
                        cp++;
                        switch(*cp)
                        {
                                case 'c':
                                        printc(*ip);
                                        break;
                                case 's':
                                        prints(*ip);
                                        break;
                                case 'd':
                                        printd(*ip);
                                        break;
                                case 'x':
                                        printx(*ip);
                                        break;
                                case 'l':
                                        printl(*ip);
                                        break;
                                default:
                                        break;
                        }               }
                cp++;
                ip++;
        }
}

У любого есть какие-нибудь подсказки, поскольку я застрял и нуждаюсь в некоторой помощи.

РЕДАКТИРОВАТЬ (2:06 вечера): я изменил все свои короткие u16/unsigned на unsigned long, и все изменилось на печать HI! :L: :0: :0: :: :: :1:

5 ответов

Решение

Для какой архитектуры вы программируете?

Если вы пишете загрузчик для x86, то ваш загрузчик сначала будет в 16-битном режиме. Таким образом, когда компилятор выдает команду push, которая, как я предполагаю, передает аргументы для функции printf(), он по умолчанию отправляет 16 бит данных. Тип данных long будет специально выданной инструкцией (или двумя), чтобы поместить все 32 бита в стек, при этом предполагается, что int равен 16 битам, а long - 32 бита (что для компилятора 16-битного режима не является необоснованным предположением Я не думаю).

Итак, предположим, что x86 в 16-битном режиме:

Может показаться, что вы используете *ip для адресации аргументов, когда они помещаются в стек. Поскольку ip является указателем на long (32-битный тип данных), когда вы делаете ip++, вы увеличиваете фактическое значение, удерживаемое указателем, на 4, как если бы *ip = 0x1234, тогда *(ip+1) = 0x1238. Таким образом, если вы используете ip++ для 16-битных целых, то вы пропускаете int каждый раз, когда делаете ip++, поскольку целые числа составляют всего 2 байта (16 бит). Возможное решение - использовать void * для ip, а затем добавить sizeof(тип данных) к ip; т.е. если вы печатаете int, значит:

void *ip = &(fmt + 1); /* Skip over fmt. */
...
ip += sizeof(int);

Или для неподписанного долго:

ip += sizeof(unsigned long);

Однако, без более подробной информации о точной архитектуре, для которой вы программируете, и о том, какой ABI использует ваш компилятор, я могу только догадываться.

Надеюсь, это поможет.

С уважением, Алекс

Вот подсказка:

>>> hex(42949672)
'0x28f5c28'
>>> hex(23592)
'0x5c28'

Где-то по пути вы используете более короткий тип. Я бы проверил ваше использование u16, например.

Предполагая, что компилятор "BCC", по крайней мере, несколько современный, вы должны использовать его <stdarg.h> макрос va_start, va_arg а также va_end чтобы получить доступ к переменным аргументам.

Современный C также требует полного прототипа с ... для функций vararg.

Вам лучше написать и проверить более простые тестовые примеры, прежде чем вы будете использовать столько аргументов%.

Ваша функция rpi использует в качестве типа данных u16 (unsigned short), который слишком мал для целого числа со значением 42949672.

Другие вопросы по тегам