问题
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 typeint
.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 typeunsigned 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