2016-07-21 5 views
0

Я работаю над платформой AVR. avr-libc не предоставляет asprintf(). Мне нужна библиотека, которую я пытаюсь внести в мой проект. Полезно, что эта же библиотека включает реализацию (ниже). К сожалению, это дает нечетные результаты. В частности, я определил, что код возврата vsnprintf() никогда не верен; а не случайные результаты, я, кажется, всегда вижу одинаковое прогрессирование неправильных значений (5, 1 и т. д.) при последовательных вызовах.Нечетное поведение vsnprintf на AVR ATmega2560

Звонок этой функции: asprintf(&str, "%s[%d]", name, val);. str - это char* в стеке в вызывающей функции. name - простой короткий текстовый дескриптор, который я проверил, не является нулевым, а не нулевым. val - простой индекс, который увеличивается в цикле, который вызывает asprintf(). Результирующая строка, создаваемая этим вызовом, должна содержать 7 символов (не считая нулевого терминатора). asprintf() называется во всей библиотеке. Мое использование содержащейся библиотеки выполняется только в этом одиночном цикле (дважды). Библиотека выполняет свою работу, как ожидалось, если я удалю этот вызов и заменим результаты фиктивными значениями. Выполнение, казалось бы, сбой при назначении буфера разыменованному ret указателю в рамках реализации asprintf().

Несмотря на множество экспериментов с буферами, разыменование указателя и управление var args, я не могу заставить эту функцию работать должным образом. Однако, скомпилированный на OS X, он работает отлично.

Я знаю, что поведение vsnprintf() не обязательно идентично для всех платформ. Тем не менее, насколько я могу судить, это использование должно работать так, как ожидалось.

Любые идеи? Может ли это быть вне самой функции? Какой-то вариант инициализации stdio или библиотеки библиотек, вызывающий проблемы?

int asprintf(char **ret, const char *fmt, ...) { 
    va_list ap1; 
    va_list ap2; 
    int count; 

    va_start(ap1, fmt); 
    va_copy(ap2, ap1); 
    count = vsnprintf(NULL, 0, fmt, ap1); 
    va_end(ap1); 

    if(count > 0) { 
    char* buffer; 

    if (!(buffer = (char*)malloc(count+1))) { 
     return -1; 
    } 
    count = vsnprintf(buffer, count+1, fmt, ap2); 
    *ret = buffer; 
    } 
    va_end(ap2); 
    return count; 
} 
+0

«прогрессирование неправильных значений (5, 1 и т. Д.) При последовательных вызовах. Каковы входы в эти успешные вызовы? Это помогло, если вы показали фактические вызовы 'asprintf'. – dxiv

+0

Хорошая точка. Вопрос обновлен, чтобы описать, как эта функция вызывается. –

+2

Объявлен ли 'asprintf' (с правильным прототипом) в заголовке, который входит в оба модуля, которые реализуют, соответственно, вызов? – dxiv

ответ

1

В предыдущих комментариях выяснилось, что asprintf был вызван без видимого прототипа в области видимости. Добавление необходимой декларации до вызова устраняет проблему.

Основная причина заключается в том, что для C требуется variadic, чтобы использовать прототип прототипа перед его использованием.

Это упоминается, например, в Are prototypes required for all functions in C89, C90 or C99?.

любой вызов к VARIADIC функции (например, Printf или зсапЕ) должны иметь видимый Prototype

некоторое представление о том, чтобы "почему" дается в comp.lang.c FAQ list - Question 15.1.

Q: Я слышал, что вы должны #include <stdio.h> перед вызовом Printf. Зачем?

A: Для того чтобы прототип printf был в полном объеме.

Компилятор может использовать другую последовательность вызовов для функций, которые принимают списки аргументов переменной длины. (Это может быть сделано, если вызовы с использованием списков аргументов переменной длины были менее эффективными, чем те, которые используют фиксированную длину.) Поэтому прототип (указывающий, используя эллипсовую нотацию «...», что список аргументов имеет переменную длина) должна быть в области видимости всякий раз, когда вызывается функция varargs, поэтому компилятор знает, как использовать механизм вызова varargs.

+1

Это хороший улов. –

0

Вы проверили определение

va_start 

макрос?

Возможно, это может вызвать проблему, поскольку адрес параметра может указывать где-то «за» указателем формата, если он не определен правильно. Если все в порядке, как вы говорите, проблема может быть в реализации vsnprintf, чем.

+0

Авторы библиотеки, в которой находится 'asprintf()', использовали var args в другом месте кода, требуя использования 'va_list',' va_start' и т. Д. Эти другие функции работают так, как ожидалось. Я смог сузить кругооборот вызовов библиотеки до содержимого 'asprintf()'. Или, скорее, проблема, похоже, проявляется здесь. Это может быть слияние внешних факторов, которые проявляются каким-то странным образом в этой функции. –

0

Нет стандартной инициализации, необходимой для stdio. Если ваша реализация требует инициализации, эта информация, мы надеемся, будет в документации.

Если vnsprintf сломана, вы можете использовать vsprintf. Вот версия списаны из FreeTDS:

int 
vasprintf(char **ret, const char *fmt, va_list ap) 
{ 
    FILE *fp; 
    if ((fp = fopen(_PATH_DEVNULL, "w")) == NULL) 
      return -1; 
    if ((fp == NULL) && ((fp = fopen(_PATH_DEVNULL, "w")) == NULL)) 
      return -1; 

    len = vfprintf(fp, fmt, ap); 

    if (fclose(fp) != 0) 
      return -1; 

    if (len < 0) 
      return len; 

    if ((buf = malloc(len + 1)) == NULL) { 
      errno = ENOMEM; 
      return -1; 
    } 
    if (vsprintf(buf, fmt, ap) != len) 
      return -1; 
    *ret = buf; 
    return len; 
} 

В зависимости от ваших потребностей, вы можете быть в состоянии повторно использовать дескриптор файла.

+0

В avr-libc реализация 'vnsprintf()' в основном представляет собой оболочку вокруг 'vsprintf()'. Когда я попытался изменить 'asprintf()', чтобы вызвать 'vsprintf()' непосредственно, я вижу такое же поведение. –

+0

В этом случае я бы опубликовал отчет об ошибке. Если вы правы, проблема глубокая и фундаментальная, и проект захочет ее исправить. Превратите свой минимальный пример в программу, которая печатает 'count' * N * раз, где * N * поступает из командной строки. Это либо проиллюстрирует вашу ошибку (если есть), либо продемонстрирует проблему достаточно. –

+0

Проблема оказалась недостатком прототипа функции для этой реализации 'asprintf()', предназначенной для покрытия платформ, в которых нет. Другой комментатор предложил это как догадка и был прав. Обработка аргументов var в списке параметров без надлежащего прототипа, вероятно, искажала стек. –

0

Я столкнулся с этим только сегодня, и, как ответил dxiv, ключ является видимым прототипом. Но я хотел бы расширить это: это не просто вариационные аргументы, которые не будут работать должным образом. В моем случае проект был создан, и функция была вызвана, но ни один из аргументов не работал должным образом. Вот очень простой пример для демонстрации. (Функция uprintf() является пользовательской функцией для распечатки через UART.)

void log_console( const char * fmtstring,...) 
{ 
    uprintf("Start of log_console\n"); 
    uprintf(fmtstring); 
} 

Вызов из основных() с прототипом скрытый:

//void log_console( char const * fmtstring,...); 
log_console("Test message to console =======\n"); 
uprintf("After test message to console\n"); 

В этом случае выход:

Start of log_console 
After test message to console 

Это показывает, что функция вызывается, но fmtstring не правильно определены. Но с прототипом видимым:

void log_console( char const * fmtstring,...); 
log_console("Test message to console =======\n"); 
uprintf("After test message to console\n"); 

функция теперь имеет доступ к входящему аргументу и строки, написанные в UART являются, как ожидалось:

Start of log_console 
Test message to console ======= 
After test message to console 

После того, как прототип был на месте , этот простой демонстрационный пример работал, а также моя полная фактическая функция, в которой использовались аргументы (...).

 Смежные вопросы

  • Нет связанных вопросов^_^