Почему мой фрагмент кода C не работает?Упрощенная версия делает.Передача аргументов без VA_ARGS для unsigned long long - PullRequest
0 голосов
/ 29 декабря 2010

В основном я пишу функцию printf для выделенной системы, поэтому я хочу передать необязательное количество аргументов без использования макросов VA_ARGS. Я набрал простой пример, и этот блок кода работает:

#include <stdio.h>

void func(int i, ...);
int main(int argc, char *argv);

int main(int argc, char *argv) {

    unsigned long long f = 6799000015ULL;
    unsigned long long *g;

    //g points to f
    g = &f;

    printf("natural: %llu in hex: %llX address: %x\n", *g, *g, g);

    //put pointer onto stack
    func(6, g, g);

    return 0;
}

void func(int i, ...) {

    unsigned long long *f;

    //pop value off
    f = *(&i + 1);

    printf("address: %x natural: %llu in hex: %llX\n", f, *f, *f);

}

Однако более крупный пример, который я пытаюсь перенести в , не работает .

(в основной функции):

unsigned long long f = 6799000015ULL;
unsigned long long *g;

g = &f;

kprintf("ull test: 1=%U 2=%X 3=%x 4= 5=\n", g, g, g);

(моя изворотливая функция printf, с которой у меня проблемы. Возможно, стоит указать этот код работает с целочисленными значениями, символьными строками или другими флагами%, которые передаются значение , а не указатель. Единственная разница между тем, что работал и неподписанным long long - один больше, поэтому вместо этого я передаю значение, чтобы не увеличивать & формат + аргументы неверно. Это имеет смысл?)

void kprintf(char *format, ...)
{
 char buffer[KPRINTF_BUFFER_SIZE];

 int bpos = 0; /* position to write to in buffer */
 int fpos = 0; /* position of char to print in format string */
 char ch; /* current character being processed*/

 /*
  * We have a variable number of paramters so we
  * have to increment from the position of the format
  * argument.
 */
 int arg_offset = 1;

 /*
  * Think this through Phill. &format = address of format on stack.
  * &(format + 1) = address of argument after format on stack.
  * void *p = &(format + arg_offset);
  * kprintf("xxx %i %s", 32, "hello");
  * memory would look like = [ 3, 32, 5, "xxx", 32, "hello" ]
  * get to 32 via p = &(format + 1); (int)p (because the int is copied, not a pointer)
  * get to hello via p = &(format + 2); (char*)p;
  */

 void *arg;
 unsigned long long *llu;
 arg = (void*) (&format + arg_offset);
 llu = (unsigned long long*) *(&format + arg_offset);

 while (1)
 {
  ch = format[fpos++];

  if (ch == '\0')
   break;

  if (ch != '%')
   buffer[bpos++] = ch;
  else
  {
   ch = format[fpos++];
   if (ch == 's')
    bpos += strcpy(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, (char*)arg);
   else if (ch == '%')
    buffer[bpos++] = '%';
   else if (ch == 'i')
    bpos += int_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
   else if (ch == 'x')
    bpos += int_to_hex_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
   else if (ch == 'o')
    bpos += int_to_oct_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
   else if (ch == 'X') {
    //arg is expected to be a pointer we need to further dereference.
    bpos += unsigned_long_long_to_hex(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
   } else if (ch == 'U') {
    bpos += unsigned_long_long_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
   } else
   {
    puts("invalid char ");
    putch(ch);
    puts(" passed to kprintf\n");
   }

   arg_offset++;
   arg = (void *)(&format + arg_offset);
   llu = (unsigned long long*) *(&format + arg_offset);
  }
 }

 buffer[bpos] = '\0';
 puts(buffer);
}

(и длинные функции без знака, которые он вызывает):

int unsigned_long_long_to_hex(char *buffer, int max_size, unsigned long long number)
{
 return ull_number_to_str(buffer, max_size, number, BASE_HEX);
}

int unsigned_long_long_to_str(char *buffer, int max_size, unsigned long long number) {
 return ull_number_to_str(buffer, max_size, number, BASE_DECIMAL);
}

int ull_number_to_str(char *buffer, int max_size, unsigned long long number, int base) {

 int bufpos = 0;

 unsigned int lo_byte = (unsigned int) number;
 unsigned int hi_byte = (unsigned int) (number >> 32);

 bufpos = number_to_str(buffer, max_size, lo_byte, base);
 bufpos += number_to_str(buffer + bufpos, max_size, hi_byte, base);

 return bufpos;

}

#define NUMERIC_BUFF_SIZE (11 * (ADDRESS_SIZE / 32))

int number_to_str(char *buffer, int max_size, int number, int base)
{
 char *char_map = "0123456789ABCDEF";

 int remain = 0;
 char buff_stack[NUMERIC_BUFF_SIZE];
 int stk_pnt = 0;
 int bpos = 0;

 /* with this method of parsing, the digits come out backwards */
 do 
 {
  if (stk_pnt > NUMERIC_BUFF_SIZE)
  {
   puts("Number has too many digits to be printed. Increasse NUMBERIC_BUFF_SIZE\n");
   return 0;
  }

  remain = number % base;
  number = number / base;
  buff_stack[stk_pnt++] = char_map[remain];
 } while (number > 0);

 /* before writing...ensure we have enough room */
 if (stk_pnt > max_size)
 {
  //error. do something?
  puts("number_to_str passed number with too many digits to go into buffer\n");
  //printf("error. stk_pnt > max_size (%d > %d)\n", stk_pnt, max_size);
  return 0;
 }

 /* reorder */
 while (stk_pnt > 0)
  buffer[bpos++] = buff_stack[--stk_pnt];

 return bpos;
}

Извините, ребята, я не вижу, что я сделал неправильно. Я ценю, что это сценарий типа «стены кода», но, надеюсь, кто-нибудь увидит, что я сделал неправильно. Я ценю, что вам, вероятно, не нравится не использовать VA_ARGS, но я не понимаю, почему этот метод не должен просто работать? А также, я тоже связываюсь с -nostdlib. Если кто-то может помочь, я действительно ценю это. Кроме того, это не должен быть производственный код качества, поэтому, если мне не хватает некоторых основ C, не стесняйтесь конструктивно относиться к нему: -)

Ответы [ 3 ]

3 голосов
/ 29 декабря 2010

Это плохая идея кодировать таким образом.Используйте stdarg.h.

Если вы случайно (я полагаю, это основано на названии kprintf), что вы работаете над хобби-ядром или встроенным проектом и не хотите использовать стандартные библиотеки, я рекомендую напо крайней мере, написание собственного (специфичного для архитектуры и компилятора) набора макросов stdarg, которые соответствуют хорошо известным интерфейсам и коду против этого.Таким образом, ваш код не будет выглядеть как WTF, разыменовываясь после адреса последнего аргумента.

Вы можете создать тип va_list, в котором будет храниться последний известный адрес, и ваш макрос va_argможет соответствующим образом выровнять sizeof типа, который он передал, и соответственно продвинуть указатель.Для большинства соглашений, над которыми я работал для x86, каждый тип продвигается до 32 бит ...

2 голосов
/ 29 декабря 2010

Я должен добавить: если вы хотите манипулировать стеком вручную, как вы делали выше, вы должны сделать это в ассемблере , а не в C. Язык C следует определенному стандартуВы делаете выше это не правовой кодекс (то есть, его значение не является четко определенным).Таким образом, компилятору разрешено делать с ним все, что он хочет, например оптимизировать его странными способами, не соответствующими вашим потребностям.

2 голосов
/ 29 декабря 2010

Вы должны прочитать соглашения о вызовах для вашей платформы, то есть, как на вашем целевом процессоре / ОС передаются аргументы функций и как сохраняются регистры.Не все параметры передаются в стеке.В зависимости от количества параметров и их типов может возникнуть много сложных ситуаций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...