Как проверить, равна ли переменная нескольким значениям? - PullRequest
2 голосов
/ 07 июля 2019

В CI я хочу проверить, равна ли переменная нескольким значениям, и я не знаю, как ее кодировать, не отделяя ее полностью.

if (str[i]=='u'||'o'||'i'||'e'||'a') давая мне всегда истину, и я не понимаю, почему,Мне нужно объяснение.

if (str[i]==('u'||'o'||'i'||'e'||'a')) давая мне всегда ложь, и я не понимаю почему, мне нужно объяснение.

спасибо.

Ответы [ 5 ]

2 голосов
/ 07 июля 2019

Вам нужно:

if (str[i] == 'u' || str[i] == 'o' || str[i] == 'i' || str[i] == 'e' || str[i] == 'a' ) {/*...*/}

A switch:

switch(str[i])
    case 'u': case 'o': case 'i': case 'e': case 'a': {/*...*/}

может иметь больше шансов дать вам лучший код (переключатели, подобные приведенным выше, использовались для эффективного лексинга с самых первых версий C), и многие люди (включая меня) также находят его более читабельным. (Многие люди находят это еще более читабельным, если вы держите дела в составном выражении {}, но я прохожу этап, когда я пропускаю их, когда могу.)

2 голосов
/ 07 июля 2019

Причина, по которой следующее выражение всегда возвращает true:

if (str[i] == 'u'||'o'||'i'||'e'||'a')

означает, что символьные константы имеют значение true. Итак, вышесказанное действительно так же, как это:

if (str[i] == 'u'|| 1 || 1 || 1 || 1)

Что вы намеревались сделать, это:

if (str[i] == 'u' || str[i] == 'o' || str[i] == 'i' || str[i] == 'e' || str[i] == 'a')

Обратите внимание, что выражение равенства необходимо повторять для каждого сравнения.

1 голос
/ 07 июля 2019

Различные результаты связаны с приоритетом оператора.

x == y || z

совпадает с

(x == y) || z

, который отличается от

x == (y || z)    

У вас есть выражение 'u'||'o'||'i'||'e'||'a', поэтому в нашем случае y будет 'u', а z будет 'o'||'i'||'e'||'a'. z будет иметь значение true, потому что хотя бы один из операндов (все они в этом случае) не равен нулю. Таким образом, первая строка будет эквивалентна (str[i] == 'u') || 1, которая, конечно, всегда будет иметь значение 1, что верно. С другой стороны, str[i] == ('u' || 1) совпадает с str[i] == 1, поскольку 'u' || 1 будет иметь значение 1.

Нет ничего хорошего в том, чтобы делать такие вещи в C. Что вы могли бы сделать, это довольно просто обобщить, это написать пользовательскую функцию, подобную этой:

bool isMember(char e, char*s, size_t size)
{
    for(size_t i; i<size; i++) {
        if(s[i] == e)
            return true;
    }
    return false;
}

Вышеуказанную функцию легко модифицировать для разных типов. Но в вашем случае это можно использовать так:

char characters[] = {'u','o','i','e','a'};
if (isMember(str[i], characters, sizeof(characters)) {

При работе с char есть несколько более простых методов, но я выбрал это решение, потому что оно не ограничено char.

0 голосов
/ 08 июля 2019

Оператор || не позволяет вам «связывать» условия таким образом. a || b || c оценивается как (a || b) || c - результат из a || b (который будет либо 0, либо 1) будет ИЛИ с c.

Для того, что вы пытаетесь сделать, самым чистым вариантом было бы использовать strchr, как предложено machine_1 в комментарии к ответу Тима Бигелайзена:

#include <string.h>
...
if ( str[i] >= 0 && strchr( "aeiou", str[i] ) )
{
  // str[i] is one of 'a', 'e', 'i', 'o', or 'u'
}

Я поставил галочку, что str[i] неотрицательный, поскольку chux утверждал, что передача отрицательного значения для str[i] в strchr приведет к неопределенному поведению; однако, смотря на стандарт, я не верю, что это правда:

7.24 Обработка строк

7.24.1 Соглашения о строковых функциях

...
3 Для всех функций в этом подпункте каждый символ должен интерпретироваться так, как если бы он имел тип <strong>unsigned char</strong> (и, следовательно, каждое возможное представление объекта является действительным и имеет другое значение).

Но мы все равно оставим это, просто ради здравомыслия.

0 голосов
/ 08 июля 2019

Объединение оператора || с несколькими значениями, такими как (str[i]=='u'||'o'||'i'||'e'||'a') или (str[i]==('u'||'o'||'i'||'e'||'a')), не используется для проверки, является ли значение одним из набора значений.

Оператор || является оператором логического ИЛИ . Он обрабатывает оба своих операнда как логические значения и оценивает их как 0 или 1 в зависимости от операндов. Использование этого оператора подробно описано в разделе 6.5.14 стандарта C :

2 Каждый из операндов должен иметь скалярный тип.

3 Оператор || должен выдавать 1, если один из его операндов не равен 0; в противном случае он возвращает 0. Результат имеет тип int.

4 В отличие от побитового оператора |, оператор || гарантирует оценку слева направо; если вычисляется второй операнд, между оценками первого и второго операндов существует точка последовательности. Если первый операнд сравнивается с неравным 0, второй операнд не оценивается.

Поскольку C не имеет истинного логического типа, любое целочисленное значение (включая символьные константы) может быть операндом ||. Таким образом, любое ненулевое значение считается истинным, а ноль - ложным. Кроме того, обратите внимание на пункт 4 выше, что этот оператор имеет оценку «короткого замыкания», что означает, что правая сторона не будет оцениваться, если результат оператора известен, просто взглянув на левую сторону.

Теперь давайте применим это к вашим выражениям. Во-первых:

if (str[i]=='u'||'o'||'i'||'e'||'a')

Поскольку здесь мы имеем дело с несколькими операторами, нам нужно применить правила приоритета операторов, детально здесь . Поскольку оператор сравнения на равенство == имеет более высокий приоритет, чем оператор логического ИЛИ ||, он анализируется следующим образом:

if ((str[i]=='u')||'o'||'i'||'e'||'a')

Итак, сначала мы оценим str[i]=='u'. Это будет 0 или 1 в зависимости от того, str[i] равно 'u' или нет. Затем мы оцениваем первое значение ||, поэтому имеем либо 1||'o', либо 0||'o'.

В первом случае левый операнд равен 1, так как согласно пункту 4 над правой стороной не оценивается, что включает в себя другие операторы ||, поэтому конечный результат равен 1, т. Е. True, что является желаемым результатом. Во втором случае 0 ложно, поэтому мы смотрим на правую сторону, которая является 'o'. Это символьная константа, значением которой является значение, используемое для кодирования символа 'o'. Если ваша система использует ASCII (что наиболее вероятно), это значение равно 111. Поскольку это ненулевое значение, все выражение 0||'o' оценивается как 1, т.е. Опять же из-за поведения короткого замыкания || следующий оператор || не оценивается, так как левая сторона истинна. Это означает, что приведенное выше выражение всегда верно.

Теперь переходим ко второму выражению:

if (str[i]==('u'||'o'||'i'||'e'||'a'))

Первое, что оценивается, это 'u'||'o'. Символ 'u' имеет код ASCII 117, который не равен нулю, поэтому первый || приводит к 1, а правая часть, которая включает в себя остальные операторы ||, не оценивается. Итак, теперь у вас есть str[i] == 1. Если str не содержит непечатаемых символов, вы никогда не найдете символ с кодировкой 1, поэтому это выражение всегда будет иметь значение 0, т. Е. False.

C не имеет встроенного оператора, который проверяет, является ли значение членом набора, что означает, что вам либо нужно явно проверить str[i] каждый символ:

if ((str[i]=='u') || (str[i]=='o') || (str[i]=='i') || (str[i]=='e') || (str[i]=='a'))

Или вы можете создать массив символов для проверки и циклического просмотра:

char vowels[5] = "aeiou";   // an array of char, but NOT a string
int found = 0;
for (int j = 0; j < sizeof(vowels); j++) {
    if (str[i] == vowels[j]) {
        found = 1;
        break;
    }
}
if (found) {
    ...

Или вы можете использовать strchr для просмотра значений:

if (strchr("aeiou", str[i]))

Или используйте switch с падениями:

switch(str[i]) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
    // do something
    break;
default:
    // do something else
}
...