Если Else падает с переменной в C

Я новичок в C, не понимаю, почему этот код не работает?

void main(){
    char userInput = "a";

    if (userInput == "a"){
        printf("a");
    } else printf("b");

    getch();
}

returning "b"

но этот работает

void main(){
    char userInput;

    if ("a" == "a"){
        printf("a");
    } else printf("b");

    getch();
}

returning "a"

В чем разница?

char userInput = "a";, ваш компилятор должен жаловаться.
Sourav Ghosh 24.09.2018 12:26

Подсказка: userInput - это единственный char, а не набор char.

CinCout 24.09.2018 12:26
"a" - это указатель (адрес памяти) на 2 байта, содержащий символ a и нулевой байт. 'a' - однобайтовая переменная. char - это тип символьной переменной. Если у вас "a" == "a", то вы сравниваете УКАЗАТЕЛИ, т.е. адреса памяти, которые просто так совпадают, поскольку компилятор поместил обе строки "a" в один и тот же адрес памяти.
KamilCuk 24.09.2018 12:28

Ни одна из частей не будет "работать" гарантированно. Строковые литералы могут иметь уникальные адреса. "a" == "a" вполне может быть ложным, AFAIK.

PSkocik 24.09.2018 12:32

Возможный дубликат Как правильно сравнивать строки?

too honest for this site 24.09.2018 13:16

@toohonestforthissite Первая половина вопроса - это абсолютный обман, а вторая часть - нет. Ответ сводится к оптимизации компилятора.

Lundin 24.09.2018 13:22

@Lundin: Обе части - дураки. Собственно вторая часть - это то, что я связал, первая часть - опечатка (char *, тогда она такая же, как вторая часть). Оба являются основами языка Си, которые объясняются очень рано в соответствующих главах книги Си для начинающих. Нет необходимости вдаваться в подробности компилятора. Это UB для сравнения указателей, которые не указывают на / точно на один и тот же объект.

too honest for this site 24.09.2018 13:24

@toohonestforthissite Ну, в этом случае код делает случайно сравнивает два указателя, указывающих на один и тот же объект.

Lundin 24.09.2018 13:27

@Lundin: Нет, они этого не делают - судя по языку. Успешное сравнение - результат UB. / Говоря об оптимизации компилятора: компилятор мог полностью заменить условие на 1).

too honest for this site 24.09.2018 13:29

@toohonestforthissite Нет, это чушь. Вы перепутали это с арифметикой указателей на указатели, которые не указывают на один и тот же объект, что здесь не применимо. В языке говорится, C17 6.5.9: Операторы равенства: «Ограничения Должно выполняться одно из следующего: ... - оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов;». Нигде нет плохо определенного поведения - сравнения указателей совершенно четко определены и подробно объяснены в C17 6.5.9 §6.

Lundin 24.09.2018 13:40

@Lundin: Черт возьми, я всегда об этом забываю. Простите, да, вы правы. Похоже на недостаток стандарта, позволяющий проводить неоднозначные сравнения, подобные приведенному выше. Но все же должно применяться 6.5.9p6: «Два указателя сравниваются как равные тогда и только тогда, когда… оба являются указателями на один и тот же объект…». Если вы не найдете в стандарте отрывок, утверждающий, что идентичные строковые литералы являются одним и тем же объектом. Если бы они просто «могли быть», это было бы двусмысленно и имо недостатком в стандарте. Я думаю, что этот абзац в любом случае довольно неаккуратный, например с последним предложением.

too honest for this site 24.09.2018 13:48

@KamilCuk: "a" - это массив из 2 символов. В контекстах, используемых OP, он преобразуется в указатель. Указатели и массивы - разные вещи.

pmg 24.09.2018 14:35

относительно: void main(){ Подпись для main() ВСЕГДА имеет тип возврата int, независимо от того, что позволяет Visual Studio

user3629249 26.09.2018 02:08

в отношении: char userInput = "a"; это пытается поместить массив в один символ (или адрес массива "a" в один символ. Предложите либо: char userInput[] = "a";, либо char *userInput = "a";. Первый инициализирует массив, второй инициализирует указатель на массив

user3629249 26.09.2018 02:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
14
141
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

Литерал, представленный как "a", является строковым литералом. Основной тип - это массив char, например char [size]. Это несовместимый тип с char, поэтому назначение

char userInput = "a";

не действует.

Вы хотите использовать символьную константу, вы пишете что-то вроде

char userInput = 'a';

Тем не менее, if ("a" == "a"), похоже, работает в вашем случае, но он не делает то, что вы думаете. Он не сравнивает содержимое, а сравнивает базовый адрес (адрес первого элемента) строк, и в вашем случае они кажутся одинаковыми. Ваш компилятор использует область памяти тем же для идентичных строковых литералов (оптимизация) - так что вы видите правдивый результат, но это не гарантируется стандартом. Компилятор (ny) может выбирать свою собственную стратегию распределения памяти с процессом оптимизации или без него.

При использовании gcc, если -fno-merge-константы используется как опция компиляции, ожидается, что это условие будет оцениваться как ложное.

Спасибо, друг за знания, не могли бы вы предложить мне какой-нибудь хороший учебник для изучения Си и информатики.

Abhishek Pandey 24.09.2018 12:40

@AbhishekPandey Нет единого источника правды. Попробуйте руки со спецификацией - трудный путь, но лучший способ,

Sourav Ghosh 24.09.2018 12:42

Цитировать символы с помощью ' Так

char userInput = 'a';

if (userInput == 'a'){

С char userInput = "a"; вы инициализируете userInput с char*, а не с символом 'a'. То же самое с сравнением, вы сравниваете char с char*.

И для инициализации, и для сравнения вы должны заключить символ в одинарные кавычки,

void main(){
    char userInput = 'a';

    if (userInput == 'a'){
        printf("a");
    } else printf("b");

    getch();
}

Если игнорировать неверный синтаксис char userInput = "a" (я так понимаю, вы имели в виду char*), то первый случай очевиден и часто встречается часто задаваемые вопросы: вы сравниваете не содержимое строк, а их адреса. См. Как правильно сравнивать строки?

Та же проблема возникает, когда вы делаете "a" == "a", вы сравниваете адреса строк. Эти доступные только для чтения константы "..." в C формально называются строковые литералы.

Компиляторы используют общепринятый метод оптимизации, известный как «объединение строк». Объединение строк означает, что, встречая один и тот же строковый литерал несколько раз в программе, компилятор выделяет только один из них.

Итак, в вашем случае "a" и "a" оказались одним и тем же адресом памяти, потому что фактически был выделен только один из строковых литералов. Вы можете попробовать распечатать фактические адреса, по которым расположены строковые литералы, и убедиться, что они идентичны:

#include <stdio.h>

int main (void) 
{
  printf("%p %p", "a", "a");  

}

Упрощенный ответ: в C все, что находится внутри двойных кавычек, является строкой, то есть последовательностью символов, оканчивающейся нулем. Одиночная кавычка предназначена для одиночного символа.

Пример :

char c = ‘a’ ;

char *str = “string”;

"a" - это строковый литерал, а не символьный литерал, который следует заключать в одинарные кавычки, как в 'a'.

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