Сохранение символа в переменной int с помощью функции scanf(), создающей неожиданное символьное эквивалентное значение int

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

Some programmer dude 20.06.2024 15:07

Всегда ошибка: не проверять возвращаемое значение от scanf.

Jens 20.06.2024 15:43

Попробуйте включить предупреждения компилятора. Я думаю, что большинство компиляторов предупредят о передаче неправильных типов указателей в scanf.

Tom Karzes 20.06.2024 16:03

Также актуально: english.stackexchange.com/questions/165598/…

s_baldur 20.06.2024 16:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы лжете, сканируя scanf("%c", &c);, говоря, что c — это char, хотя на самом деле это int. Это неопределенная ошибка поведения, поэтому может случиться что угодно.

Один из вероятных результатов (не гарантированный, но вполне вероятный) заключается в том, что байт считывается по младшему адресу int. В случае машины с прямым порядком байтов она будет работать нормально, поскольку ожидает, что числа от 0 до 255 в этом байте будут напрямую соответствовать значениям от 0 до 255. Таким образом, результат int в конечном итоге будет таким, как заданное значение. что все остальные байты int равны нулю. Или, альтернативно, на машине с прямым порядком байтов вы будете записывать самый старший байт, что приведет к очень большому числу.

Однако вы никогда не инициализировали c, поэтому эти другие байты могут содержать мусорные значения. Или, возможно, нули, если вам не повезло - многие отладочные сборки обнуляют локальные переменные, которые не инициализированы, что ни в коем случае не является какой-либо гарантией со стороны C. Печать содержимого локальной переменной, у которой не указан ее адрес, также является неопределенным поведением. Итак, вы записали адрес во время вызова scanf. Но если предположить, что это основная система без представлений ловушек для int, это по-прежнему неопределенное поведение - то есть может произойти любая комбинация, и вы можете получить любое значение, но, по крайней мере, программа не выйдет из строя и не сгорит неожиданно, как в случае с неопределенным поведением.

Обратите внимание, что все современные оптимизирующие компиляторы C принимают пункт J.2 «[поведение не определено, если] значение объекта <del>с автоматическим сроком хранения</del> используется, пока оно неопределенно» как нормативное. и как-будто зачеркнутых слов не было, несмотря на то, что это не подкреплено реальным нормативным текстом стандарта, а значит, печать содержимого неинициализированной переменной любого вида - это UB независимо от того, был ли занят ее адрес.

zwol 20.06.2024 16:14

@zwol Да, приложение J не работает, и комитет знает о ситуации с «шаткими ценностями», которая до сих пор не урегулировалась в C23. Например, я помню, как задавал эти вопросы, из которых мы могли сделать вывод, что цель clang — стать реализацией знаменитой «ISO-совместимой станции смерти 9000» — она внезапно остановила генерацию кода на середине исходного кода C только потому, что столкнулась с неопределенной ценить.

Lundin 20.06.2024 16:34

@zwol ... в этот момент не имеет особого значения, что говорят стандарты C, если амбиция сопровождающих компилятора состоит в том, чтобы создать бесполезный, но потенциально совместимый с ISO продукт, с целью удовлетворения эго сопровождающих компилятора. А не с амбициями создать качественную часть программного обеспечения с открытым исходным кодом на благо человечества, которое делает все возможное, чтобы оставаться верным намерениям программиста превыше всего.

Lundin 20.06.2024 16:36

@Lundin Раньше я работал с этими людьми, и я совершенно уверен, что, насколько они обеспокоены, в той степени, в которой фактический нормативный текст не поддерживает их предпочтительную семантику для неопределенных значений, это дефектный стандарт. И в этом конкретном случае я с ними согласен: никогда не бывает веской причины хотеть прочитать неопределенное значение, и если предположить, что этого никогда не произойдет, оптимизация реального кода действительно улучшается. (В отличие, скажем, от очень удивительного подхода Clang к преднамеренным бесконечным циклам.)

zwol 20.06.2024 21:50

@zwol Я программирую очень низкоуровневые вещи, и на это есть причины. Например, вы можете захотеть настроить сегмент ОЗУ, который сохраняется после предыдущего выполнения, непосредственно после сброса сторожевого таймера. У вас будет код, который проверяет, было ли причиной сброса включение питания = инициализировать эту область ОЗУ, или это была другая причина сброса = не трогайте ее, а прочитайте.

Lundin 21.06.2024 22:50

@zwol Но если оставить в стороне такие особые случаи, если мне случится написать ошибку, которая случайно считывает неопределенное значение, я был бы признателен, если бы все, что произойдет, - это то, что моя программа напечатает какой-то мусор. Вместо того, чтобы яростно зависнуть и стать бесполезным PoS. Пользователь моей программы согласен. Предположим, что часть, считывающая неопределенное значение, вообще не важна, но остальная часть программы очень важна — части, которые clang высокомерно отказывались даже генерировать — их даже нет в моем исполняемом файле. Просто потому, что у меня была небольшая ошибка в какой-то функции фоновой регистрации или что-то в этом роде.

Lundin 21.06.2024 22:52

Другие вопросы по теме