scanf перезаписывает вторую переменную - PullRequest
4 голосов
/ 23 мая 2019

Я прошу пользователя ввести две переменные. Сначала без знака целое число a, а затем без знака b. Показание в a работает нормально, но после чтения в b, a равно 0.

Я обнаружил, что указатель на a на единицу больше, чем указатель на b. Я понял, что когда b больше 255 a больше не равно 0. Поэтому мне кажется, что scanf читает более одного байта для b и перезаписывает a.

#include <stdio.h>

int main ()
{
    unsigned int a;
    unsigned char b;

    printf("a: ");
    scanf("%u", &a); /* 255 */
    printf("b: ");
    scanf("%hhu", &b); /* 17 */

    printf("a: %u\n", a); /* a: 0 */
    printf("b: %u\n", b); /* b: 17 */

    printf("pointer a: %u\n", &a); /* pointer a: 6422316 */
    printf("pointer b: %u\n", &b); /* pointer b: 6422315 */

    return 0;
}

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

gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/8.2.0/lto-wrapper.exe
Target: mingw32
Configured with: ../src/gcc-8.2.0/configure --build=x86_64-pc-linux-gnu --host=mingw32 --target=mingw32 --prefix=/mingw --disable-win32-registry --with-arch=i586 --with-tune=generic --enable-languages=c,c++,objc,obj-c++,fortran,ada --with-pkgversion='MinGW.org GCC-8.2.0-3' --with-gmp=/mingw --with-mpfr=/mingw --with-mpc=/mingw --enable-static --enable-shared --enable-threads --with-dwarf2 --disable-sjlj-exceptions --enable-version-specific-runtime-libs --with-libiconv-prefix=/mingw --with-libintl-prefix=/mingw --enable-libstdcxx-debug --with-isl=/mingw --enable-libgomp --disable-libvtv --enable-nls --disable-build-format-warnings
Thread model: win32
gcc version 8.2.0 (MinGW.org GCC-8.2.0-3)

Ответы [ 2 ]

5 голосов
/ 23 мая 2019

Это почти дубликат различных вопросов о печати значения size_t с %zu в коде, скомпилированном с MinGW.Оба модификатора длины z и hh появились в C99.

C89 описал модификаторы длины следующим образом

  • Необязательный h, l (ell) или L указывает размер принимающего объекта.Спецификаторам преобразования d, i и n должен предшествовать h, если соответствующий аргумент является указателем на short int, а не указателем на int, или l, если онуказатель на long int.Аналогично, спецификаторам преобразования o, u и x должен предшествовать h, если соответствующий аргумент является указателем на unsigned short int, а не указателем на unsigned int, или l, еслиэто указатель на unsigned long int.Наконец, спецификаторам преобразования e, f и g должен предшествовать K, если соответствующий аргумент является указателем на double, а не указателем на float, или L, если онуказатель на long double.Если h, l или L появляется с любым другим спецификатором преобразования, поведение не определено.

Модификатор длины hh появился в C99. C11 7.21.6.1p7 говорит:

hh

Указывает, что следующий d, i, *Спецификатор преобразования 1057 *, u, x или X применяется к аргументу со знаком char или unsigned (аргумент будет повышен в соответствии с целочисленными предложениями, но его значение должно быть преобразовано в знаковый char или unsignedсимвол перед печатью);или что следующий спецификатор преобразования n применяется к указателю на аргумент со знаком char.

MinGW использует MSVCRT.DLL в качестве библиотеки C - эта библиотека поддерживает только C89.При просмотре %hhu поведение составляет undefined в соответствии с C89, но это согласуется с интерпретацией этого как %hu и записью значения unsigned short в 2 байтов, начиная с&a.


Решение состоит в том, чтобы прочитать значение во временную unsigned short или unsigned int, а затем присвоить его unsigned char.

5 голосов
/ 23 мая 2019

Проблема в этой строке:

    scanf("%hhu", &b); /* 17 */

Библиотека времени выполнения Microsoft C не поддерживает %hhu (до C99 она не была частью стандарта C).Это приводит к тому, что входные данные интерпретируются как short (из-за того, как MSVCRT интерпретирует hh как h в спецификаторе формата), и записывает данные за пределами b и перезаписывает части a.

Чтобы решить эту проблему, измените тип a на unsigned short и используйте %hu в качестве спецификатора формата ИЛИ передайте флаг -D__USE_MINGW_ANSI_STDIO вашему компилятору.

...