Соответствующая часть фрагмента кода:
char T[2];
scanf("%s", &T);
&T
- указатель на массив из двух символов (char (*)[2]
). Это не тот тип, который нужен scanf
для спецификатора %s
: ему нужен указатель на символ (char *
). Так что поведение программы не определено.
Правильный способ написать эту программу, как вы знаете, это
char T[2];
scanf("%s", T);
Поскольку T
является массивом, когда он используется в большинстве контекстов, он «разлагается» на указатель на первый символ: T
эквивалентен &(T[0])
, который имеет тип char *
. Это затухание не происходит, когда вы берете адрес массива (&T
) или его размер (sizeof(T)
).
На практике почти все платформы используют одинаковое представление для всех указателей на один и тот же адрес. Таким образом, компилятор генерирует точно такой же код для T
и &T
. Есть несколько редких платформ, которые могут генерировать другой код (я слышал о них, но не мог назвать одну). Некоторые платформы используют разные кодировки для «байтовых указателей» и «словесных указателей», потому что их процессор изначально адресует слова, а не байты. На таких платформах int *
и char *
, указывающие на один и тот же адрес, имеют разные кодировки. Преобразование между этими типами преобразует значение, но неправильное использование в чем-то вроде списка аргументов переменной может привести к неверному адресу. Однако я ожидаю, что такие платформы будут использовать байтовые адреса для массива символов. Существуют также редкие платформы, где указатель кодирует не только адрес данных, но также некоторую информацию о типе или размере. Однако на таких платформах информация о типе и размере должна быть эквивалентной: это блок из 2 байтов, начиная с адреса T
, и адресуемый байт за байтом. Так что эта конкретная ошибка вряд ли окажет какое-либо практическое влияние.
Обратите внимание, что было бы совсем иначе, если бы у вас был указатель вместо массива во-первых:
char *T; // known to point to an array of two characters
scanf("%s", &T); // bad
Здесь &T
- указатель на место в памяти, которое содержит адрес массива символов. Таким образом, scanf
записывает символы, которые он читает, в том месте, где указатель T
хранится в памяти, а не в том месте, на которое указывает T
. Большинство компиляторов анализируют строку формата функций, таких как printf
и scanf
, и поэтому выдают сообщение об ошибке.
Обратите внимание, что char T[2]
имеет место только для двух символов, и это включает нулевой байт в конце строки. Так что scanf("%s", T)
имеет место только для чтения одного символа. Если на этом этапе входные данные содержат более одного непробельного символа, программа переполнит буфер. Чтобы прочитать один символ и сделать его односимвольной строкой, используйте
char T[2];
scanf("%c", T);
T[1] = 0;
В отличие от scanf("%s", T)
, он читает любой символ, даже пробел. Чтобы прочитать строку с ограничением длины, добавьте ограничение к спецификации %s
. Никогда не следует использовать неограниченное число %s
в scanf
, поскольку при этом будет считано столько входных данных, сколько доступно, независимо от того, сколько места есть для хранения этого ввода в памяти.
char T[2];
scanf("%1s", T); // one less than the array size