How to check if variable equal to multiple values

这一生的挚爱 提交于 2020-06-27 16:47:19

问题


In C I want to check if variable equal to multiple values and I don't know how to code it without separating it fully.

if (str[i]=='u'||'o'||'i'||'e'||'a') giving me always true and I don't understand why, I need explanation.

if (str[i]==('u'||'o'||'i'||'e'||'a')) giving me always false and I don't understand why, I need explanation.

thanks.


回答1:


The reason why the following expression is always returning true:

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

is that character constants evaluate to true. So, the above is really the same as this:

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

What you intended to do is this:

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

Note that the equality expression needs to be repeated for each comparison.




回答2:


You need:

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': {/*...*/}

might have a better chance of giving you better code (switches like the one above have been used for efficient lexing since the very first versions of C) and lots of people (including me) find it more readable too. (Lots of people find it even more readable if you keep the cases inside a {} compound statement, but I'm going through a phase where I leave them out whenever I can.)




回答3:


The different results has to do with operator precedence.

x == y || z

is the same as

(x == y) || z

which is different from

x == (y || z)    

You have the expression 'u'||'o'||'i'||'e'||'a' so in our case, y will be 'u' and z will be 'o'||'i'||'e'||'a'. z will evaluate to true, because at least one of the operands (all of them in this case) is non-zero. So the first line will be equivalent to (str[i] == 'u') || 1 which of course always will evaluate to 1, which is true. On the other hand, str[i] == ('u' || 1) is the same as str[i] == 1 because 'u' || 1 will evaluate to 1.

There is no good built in way to do such a thing in C. What you could do, that is pretty easy to generalize is to write a custom function like this:

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

The above function is easy to modify for different types. But in your case it can be used like this:

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

When dealing with char there are somewhat easier methods, but I chose this solution because it is not restricted to char.




回答4:


Chaining the || operator with multiple values like (str[i]=='u'||'o'||'i'||'e'||'a') or (str[i]==('u'||'o'||'i'||'e'||'a')) is not used to check if a value is one of a set of values.

The || operator is the logical OR operator. It treats both of its operands as boolean values and evaluates to either 0 or 1 depending on the operands. The use of this operator is detailed in section 6.5.14 of the C standard:

2 Each of the operands shall have scalar type.

3 The || operator shall yield 1 if either of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.

4 Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.

Since C doesn't have a true boolean type, any integer value (which includes character constants) can be an operand to ||. So any non-zero value is considered true and zero is considered false. Also, note from paragraph 4 above that this operator has "short-circut" evaluation, meaning that the right side won't be evaluated if the result of the operator is known just by looking at the left side.

Now lets apply this to your expressions. First:

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

Because we're dealing with multiple operators here, we need to apply the operator precedence rules detailed here. Since the equality comparison operator == has higher precedence than the logical OR operator ||, this parses as follows:

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

So first we evaluate str[i]=='u'. This will be either 0 or 1 depending on whether str[i] is 'u' or not. Then we evaluate the first ||, so we have either 1||'o' or 0||'o'.

In the first case the left operand is 1 so as per paragraph 4 above the right side is not evaluated, which includes the other || operators so the final result is 1, i.e. true which is the desired result. In the second case 0 is false so then we look at the right side which is 'o'. This is a character constant whose value is value used to encode the character 'o'. If your system uses ASCII (which it most likely does) this value is 111. Because this is a non-zero value the whole expression 0||'o' evaluates to 1 i.e. true. Again because of the short-circuit behavior of || the next || operator isn't evaluated since the left side is true. This means the above expression is always true.

Now moving to your second expression:

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

The first thing that is evaluated is 'u'||'o'. The 'u' character has an ASCII code of 117 which is non-zero, so the first || results in 1 and the right side, which includes the remaining || operators are not evaluated. So now you have str[i] == 1. Unless str contains non-printable characters, you'll never find a character whose encoding is 1, so this expression will always evaluates to 0, i.e. false.

C doesn't have a built in operator that checks if a value is a member of a set, which means you either need to check str[i] each character explicitly:

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

Or you can create an array of characters to check and loop through them:

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) {
    ...

Or you can use the strchr to loop through the values for you:

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

Or use a switch with fallthrough cases:

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



回答5:


The || operator doesn't allow you to "chain" conditions that way. a || b || c is evaluated as (a || b) || c - the result of a || b (which will be either 0 or 1) will be OR'd with c.

For what you're trying to do, the cleanest option would be to use strchr as suggested by machine_1 in a comment to Tim Biegeleisen's answer:

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

I put a check that str[i] is non-negative since chux claimed that passing a negative value for str[i] to strchr would result in undefined behavior; however, looking at the standard, I don't believe that's true:

7.24 String handling <string.h>

7.24.1 String function conventions

...
3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).

But we'll leave it in anyway, just for sanity's sake.



来源:https://stackoverflow.com/questions/56922943/how-to-check-if-variable-equal-to-multiple-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!