Что не так с printf(%llx)?

У меня есть этот фрагмент кода, который бросает вызов всем моим знаниям о C. Вот у меня есть:

int main(void){
    unsigned long long int massage ;

    scanf("%llX", &massage); //input: 0x1234567890abcdef
    printf("%llX", massage);
    return 0;
}

На моем «64-битном — Corei5 — Fedora — GCC» он печатает именно то, что я ему накормил. но в системе моего приятеля (32-битная, MS XP, MinGW) он печатает 90ABCDEF. Я не понимаю, почему. кто-нибудь знает?

Кстати: sizeof(unsigned long long int) в его системе равно 8.


person Untitled    schedule 28.12.2012    source источник
comment
Он использует MSVC или около того? Они имеют нестандартные форматы printf и scanf. Должен быть найден в документации компилятора.   -  person Daniel Fischer    schedule 28.12.2012
comment
он использует MINGW. (отредактировал вопрос)   -  person Untitled    schedule 28.12.2012
comment
Использует ли MinGW собственную библиотеку C или библиотеку Windows? В последнем случае по-прежнему применяются нестандартные форматы. В первом без понятия.   -  person Daniel Fischer    schedule 28.12.2012
comment
@DanielFischer Связано: stackoverflow.com/questions/13590735/ Судя по всему, он использует Windows.   -  person    schedule 28.12.2012


Ответы (2)


Проблема заключается в несоответствии между тем, во что верит компилятор (как отражено в sizeof: sizeof(unsigned long long int) оценивается во время компиляции) и во что верит библиотека времени выполнения (как отражено в printf: функция printf вызывается во время выполнения, так что это когда его спецификаторы формата вступают в силу).

Согласно C99 в документации MinGW:

GCC не включает библиотеку времени выполнения C. Это обеспечивается платформой. Порт MinGW для GCC использует исходную (старую) среду выполнения Microsoft Visual C, MSVCRT, на которую нацелена Microsoft Visual Studio 6 (выпущенная в 1998 году).

[…]

Поскольку MinGW основан на MSVCRT, он имеет многие из тех же ограничений и особенностей совместимости, что и Visual Studio 6. Вы должны исходить из того, что приложения MinGW не могут полагаться на поведение C99, а только на C89. Например, новые символы формата в printf, такие как %a и %ll, не поддерживаются, хотя существует обходной путь для %ll.

(Обходной путь, который он упоминает, заключается в использовании I64 вместо ll: таким образом, %I64X. Раздражает, по крайней мере, в моей системе, GCC выдаст предупреждение, когда увидит это в буквальной строке формата, потому что предполагает, что он будет иметь лучшая библиотека времени выполнения.)

person ruakh    schedule 28.12.2012
comment
Вы можете использовать конкатенацию строк препроцессора, чтобы обойти предупреждение GCC. Это не улучшит внешний вид вашего кода, но, по крайней мере, он будет использовать правильный код для правильной библиотеки. - person ; 28.12.2012
comment
@Tinctorius: Обойти предупреждение не так сложно — например, вы можете просто сохранить строку формата во временной переменной — просто необходимость этого раздражает. :-П - person ruakh; 28.12.2012
comment
По-прежнему приятно, когда ваш компилятор проверяет строку формата. В конце концов, правильность строки формата зависит от других аргументов printf и scanf, так что в некотором смысле это часть функции. Вместо этого вы можете создать константу препроцессора HEX64, которая преобразуется в "%I64X" для сборок MSVCRT и "%llX" для сборок, подобных POSIX. Затем вы можете написать printf("I like the number " HEX64 "!", number), и GCC все еще может проверить, согласуется ли тип number со строкой формата. - person ; 28.12.2012
comment
@Tinctorius: Извините, я думаю, что мы неправильно общаемся. Я имею в виду, что когда я работаю в системе Windows с MinGW и использую литеральную строку с %I64X, GCC выдаст мне предупреждающее сообщение, потому что он не распознает эту нотацию. Он просто ничего не знает о библиотеке времени выполнения, поэтому предполагает, что она POSIX-y. (То есть предупреждения GCC пытаются заставить меня использовать нотации POSIX, даже когда я работаю в системе, где нотации POSIX не будут работать. Что имеет смысл, но раздражает.) - person ruakh; 28.12.2012
comment
Понимаю. Тогда это вина GCC за то, что он не сделал эту проверку легко расширяемой. Или на самом деле MSVCRT за то, что это плохая библиотека. - person ; 28.12.2012
comment
Нет необходимости беспокоиться об этом. MinGW предоставляет в своей копии inttypes.h набор совместимых с Windows определений для спецификаторов типа C99 printf, поэтому, если вы используете спецификаторы, соответствующие C99, вы избежите этих проблем. Вместо printf("%llx\n", foo) просто #include <inttypes.h> и используйте printf("%" PRIx64 "\n", foo); MinGW сделает его %I64x, а UNIX-подобные платформы сделают его %llx. Вам также может понадобиться передать --std=gnu99 в GCC. - person Jody Bruchon; 13.02.2017
comment
@JodyLeeBruchon: Спасибо! Вы знаете, когда это было добавлено? (То есть: как человек может легко сказать, будет ли это работать в данной системе, если не опробовать ее?) - person ruakh; 13.02.2017
comment
Это часть стандарта C99, поэтому он был добавлен в стандарт в 1999 году. Многие компиляторы придерживаются стандарта C89 по умолчанию (я думаю, что это было изменено в GCC по умолчанию на C11 по умолчанию довольно недавно), но это не синтаксический или ключевое слово, это заголовок, поэтому он должен работать на любом компиляторе, совместимом с C99. inttypes.h поддерживается всеми современными системами, кроме MSVC++; все UNIX-подобные системы, охватываемые GCC или Clang, будут иметь его, как и все ответвления MinGW для Windows. Для MSVC++ вы можете использовать это: github.com/mattn /gntp-send/blob/master/include/msinttypes/ - person Jody Bruchon; 14.02.2017
comment
Используйте -D__USE_MINGW_ANSI_STDIO=1, чтобы использовать настоящий stdio вместо дерьмового окна. - person Shahbaz; 03.11.2017

Библиотека Windows C использует "%I64d", а не "%lld", для вывода аргументов типа "long long".

Ссылка: http://gcc.gnu.org/ml/gcc-patches/2004-11/msg01966.html

person Rubens    schedule 28.12.2012