Я проверял библиотечную функцию isupper(), чтобы проверить, является ли версия функции или версия макроса isupper() эффективен в зависимости от используемого хранилища или времени выполнения, и у меня есть эта своеобразная проблема при использовании scanf() для сохранения входного символа в переменной int. Вот мой код-
#include<stdio.h>
int my_isupper(int );
int main() {
int c;
printf("Enter a alphabet to check if it is upper case or not\n");
//c = getchar(); // getchar(), getc() works fine though
scanf("%c", &c);
/*
Doesn't work when c is a int but not initialized to 0
Works fine on some machine no matter c is a char, int or not initialized to 0
Is it compiler/machine dependent? if so, which part ( scanf()?) has dependency?
*/
printf("test1: %d\n", c);
if (my_isupper(c))
printf("%c is upper case\n", c);
else
printf("%c is not upper case\n", c);
return 0;
}
int my_isupper(int c) {
printf("test2: %c\n", c);
int value = (c >= 'A' && c <= 'Z')? 1 : 0;
printf("test3: %d\n", value);
return value;
}
когда переменная c установлена как char, она работает нормально. когда он установлен как int, программа работает нормально с библиотечными функциями getchar(), getc() и т. д., но при использовании scanf(), если переменная установлена как int и не инициализирован значением 0, тогда scanf() сохраняет 32577 для символа 'A', 32578 для символ «B» и так далее.
При вводе ввода: A возвращаемое значение должно быть 1, но возвращаемое значение, которое я получаю, равно 0, поскольку условие не выполняется, поскольку функция scanf() сохраняет 32577 для символа A и 32778 для символа B. и так далее.
Всегда ошибка: не проверять возвращаемое значение от scanf.
Попробуйте включить предупреждения компилятора. Я думаю, что большинство компиляторов предупредят о передаче неправильных типов указателей в scanf
.
Также актуально: english.stackexchange.com/questions/165598/…
Вы лжете, сканируя scanf("%c", &c);
, говоря, что c
— это char
, хотя на самом деле это int
. Это неопределенная ошибка поведения, поэтому может случиться что угодно.
Один из вероятных результатов (не гарантированный, но вполне вероятный) заключается в том, что байт считывается по младшему адресу int
. В случае машины с прямым порядком байтов она будет работать нормально, поскольку ожидает, что числа от 0 до 255 в этом байте будут напрямую соответствовать значениям от 0 до 255. Таким образом, результат int
в конечном итоге будет таким, как заданное значение. что все остальные байты int
равны нулю. Или, альтернативно, на машине с прямым порядком байтов вы будете записывать самый старший байт, что приведет к очень большому числу.
Однако вы никогда не инициализировали c
, поэтому эти другие байты могут содержать мусорные значения. Или, возможно, нули, если вам не повезло - многие отладочные сборки обнуляют локальные переменные, которые не инициализированы, что ни в коем случае не является какой-либо гарантией со стороны C. Печать содержимого локальной переменной, у которой не указан ее адрес, также является неопределенным поведением. Итак, вы записали адрес во время вызова scanf. Но если предположить, что это основная система без представлений ловушек для int
, это по-прежнему неопределенное поведение - то есть может произойти любая комбинация, и вы можете получить любое значение, но, по крайней мере, программа не выйдет из строя и не сгорит неожиданно, как в случае с неопределенным поведением.
Обратите внимание, что все современные оптимизирующие компиляторы C принимают пункт J.2 «[поведение не определено, если] значение объекта <del>с автоматическим сроком хранения</del> используется, пока оно неопределенно» как нормативное. и как-будто зачеркнутых слов не было, несмотря на то, что это не подкреплено реальным нормативным текстом стандарта, а значит, печать содержимого неинициализированной переменной любого вида - это UB независимо от того, был ли занят ее адрес.
@zwol Да, приложение J не работает, и комитет знает о ситуации с «шаткими ценностями», которая до сих пор не урегулировалась в C23. Например, я помню, как задавал эти вопросы, из которых мы могли сделать вывод, что цель clang — стать реализацией знаменитой «ISO-совместимой станции смерти 9000» — она внезапно остановила генерацию кода на середине исходного кода C только потому, что столкнулась с неопределенной ценить.
@zwol ... в этот момент не имеет особого значения, что говорят стандарты C, если амбиция сопровождающих компилятора состоит в том, чтобы создать бесполезный, но потенциально совместимый с ISO продукт, с целью удовлетворения эго сопровождающих компилятора. А не с амбициями создать качественную часть программного обеспечения с открытым исходным кодом на благо человечества, которое делает все возможное, чтобы оставаться верным намерениям программиста превыше всего.
@Lundin Раньше я работал с этими людьми, и я совершенно уверен, что, насколько они обеспокоены, в той степени, в которой фактический нормативный текст не поддерживает их предпочтительную семантику для неопределенных значений, это дефектный стандарт. И в этом конкретном случае я с ними согласен: никогда не бывает веской причины хотеть прочитать неопределенное значение, и если предположить, что этого никогда не произойдет, оптимизация реального кода действительно улучшается. (В отличие, скажем, от очень удивительного подхода Clang к преднамеренным бесконечным циклам.)
@zwol Я программирую очень низкоуровневые вещи, и на это есть причины. Например, вы можете захотеть настроить сегмент ОЗУ, который сохраняется после предыдущего выполнения, непосредственно после сброса сторожевого таймера. У вас будет код, который проверяет, было ли причиной сброса включение питания = инициализировать эту область ОЗУ, или это была другая причина сброса = не трогайте ее, а прочитайте.
@zwol Но если оставить в стороне такие особые случаи, если мне случится написать ошибку, которая случайно считывает неопределенное значение, я был бы признателен, если бы все, что произойдет, - это то, что моя программа напечатает какой-то мусор. Вместо того, чтобы яростно зависнуть и стать бесполезным PoS. Пользователь моей программы согласен. Предположим, что часть, считывающая неопределенное значение, вообще не важна, но остальная часть программы очень важна — части, которые clang высокомерно отказывались даже генерировать — их даже нет в моем исполняемом файле. Просто потому, что у меня была небольшая ошибка в какой-то функции фоновой регистрации или что-то в этом роде.
Формат
scanf
%c
предполагаетchar *
. Несовпадение формата и типа аргумента приводит к неопределенному поведению.