Таинственное поведение sprintf в C: Второй параметр lld всегда печатается как ноль (0) - PullRequest
3 голосов
/ 15 апреля 2011

Я сталкиваюсь с совершенно странной проблемой со sprintf в C, которая выходит за рамки моих базовых способностей к отладке.По сути, я использую простую структуру модульных тестов (CuTest), чтобы добавить некоторые тесты в некрасивую (недокументированную, без модульных тестов) базу кода.Я добавил новый тип модульного теста, который в основном дублирует уже существующие, но для 64-битных целых чисел, используемых в коде.

Этот тест в целом работает (правильно оценивает сравнения на равенство), но при неудаче не выдает правильное сообщение об ошибке из-за проблемы sprintf.Функция выглядит следующим образом:

/* Comparison Function for U64 Numbers */
void CuAssertU64Equals_LineMsg(CuTest* tc, const char* file, int line, const char* message, u64 expected, u64 actual) {
    char buf[STRING_MAX];
    if (expected == actual) return;
    sprintf(buf, "expected <%lld> but was <%lld>", expected, actual);
    CuFail_Line(tc, file, line, message, buf);
}

(Примечание: STRING_MAX равен 512, поэтому он должен быть достаточно большим).(Примечание 2: В системе Cygwin, с которой я сейчас работаю, u64 - это переменная «long long int»)

Если этот тест не пройден, полученное сообщение об ошибке является странной частью.Независимо от значения «фактического», он печатает 0 в этом месте.Таким образом, с учетом ожидаемого = 1 и фактического = 2 сообщение будет выглядеть следующим образом:

"expected <1> but was <0>"

Если вы измените положения аргументов и сделаете так, чтобы в них было что-то вроде:

sprintf(buf, "actually <%lld> but expected <%lld>", actual, expected);

Выполучим вывод:

"actually <2> but expected <0>"

Излишне говорить, что это имеет очень мало смысла и, возможно, указывает на какую-то странную ошибку стека, может быть?Честно говоря, я просто не совсем понимаю, как такая ошибка может произойти, даже в принципе.Я сделал небольшой рабочий пример с кодом CuTest, и он работал должным образом (не устанавливал второй в ноль).Это указывает на то, что ни CuTest, ни сама функция не являются проблемой.

Однако при использовании с реальной базой кода возникает эта проблема.Проблема связана со средой (стеком, памятью или переменными).

Кто-нибудь знает, почему это может произойти?Мои текущие теории-кандидаты: 1. Переполнение / переполнение в функции sprintf при попытке чтения данных.Я не уверен, как это произойдет, поскольку любые данные, передаваемые в функцию, являются значениями.Более того, сами данные явно существуют - я могу видеть каждое значение, если меняю порядок.

  1. Я каким-то образом использую неправильное форматирование.Я был почти уверен, что lld был верным для длинного длинного целого (и работает в моем минимальном рабочем примере), но, возможно, он хрупок.

  2. Полная ошибка в стеке какого-то рода.Надеюсь, что это не так, потому что я работаю над этим проектом 20 часов в неделю.Я сомневаюсь, что смог бы отладить всю кодовую базу, чтобы выяснить что-то такого масштаба.

В настоящее время я компилирую с использованием gcc-3 в среде cygwin, чего бы это ни стоило.Любые предположения были бы хорошими, я знаю, что в принципе невозможно точно диагностировать это, не видя всей базы кода, но даже некоторые рекомендации по отладке такого рода проблем были бы хороши.

1 Ответ

4 голосов
/ 15 апреля 2011

Вместо этого используйте "% I64d" в качестве строки формата. Я не уверен в Cygwin, но я знаю ссылки MinGW в функции printf из стандартной библиотеки Visual Studio, что приводит к разного рода разрушениям. Остерегайтесь любых новых функций и типов C99, таких как длинные дубликаты или форматы size_t.

Для чего стоит вот замена printf в этом контексте:

static char *CuPrintU64(char* buffer, u64 value) {
    do
        *--buffer = value % 10 + '0';
    while(value /= 10);
    return buffer;
}

/* Comparison Function for U64 Numbers */
void CuAssertU64Equals_LineMsg(CuTest* tc, const char* file, int line, const char* message, u64 expected, u64 actual) {
    static const char first[] = "expected ";
    static const char second[] = " but was ";

    char buf[STRING_MAX], *ptr = &buf[sizeof buf];

    if(expected == actual) return;

    /* sprintf(buf, "expected <%llu> but was <%llu>", expected, actual); */
    *--ptr = '\0';
    ptr = CuPrintU64(ptr, actual);
    ptr = memcpy(ptr - second, sizeof second - 1);
    ptr = CuPrintU64(ptr, expected);
    ptr = memcpy(ptr - first, sizeof first - 1);

    CuFail_Line(tc, file, line, message, ptr);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...