Я проверял библиотечную функцию 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 *. Несовпадение формата и типа аргумента приводит к неопределенному поведению.