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.