【发布时间】:2019-11-17 06:16:57
【问题描述】:
在 C 中 我想检查变量是否等于多个值,我不知道如何在不完全分离的情况下对其进行编码。
if (str[i]=='u'||'o'||'i'||'e'||'a') 给我的总是真实的,我不明白为什么,我需要解释。
if (str[i]==('u'||'o'||'i'||'e'||'a')) 总是给我假,我不明白为什么,我需要解释。
谢谢。
【问题讨论】:
标签: c logical-operators
在 C 中 我想检查变量是否等于多个值,我不知道如何在不完全分离的情况下对其进行编码。
if (str[i]=='u'||'o'||'i'||'e'||'a') 给我的总是真实的,我不明白为什么,我需要解释。
if (str[i]==('u'||'o'||'i'||'e'||'a')) 总是给我假,我不明白为什么,我需要解释。
谢谢。
【问题讨论】:
标签: c logical-operators
|| 运算符不允许您以这种方式“链接”条件。 a || b || c 被评估为 (a || b) || c - a || b(将为 0 或 1)的 结果 将与 c 进行或运算。
对于您正在尝试做的事情,最干净的选择是使用strchr,正如 machine_1 在对 Tim Biegeleisen 的回答的评论中所建议的那样:
#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>(因此每个可能的对象表示都是有效的,并且具有 不同的值)。
但无论如何我们都会把它留下来,只是为了保持理智。
【讨论】:
将|| 运算符与(str[i]=='u'||'o'||'i'||'e'||'a') 或(str[i]==('u'||'o'||'i'||'e'||'a')) 等多个值链接起来不用于检查一个值是否是一组值中的一个。
|| 运算符是逻辑或运算符。它将其两个操作数都视为布尔值,并根据操作数计算为 0 或 1。这个运算符的使用在C standard的第6.5.14节中有详细说明:
2 每个操作数都应该是标量类型。
3 如果
||运算符的任一操作数比较不等于0,则该运算符将产生1;否则,它产生 0。结果的类型为int。4 与按位的
|运算符不同,||运算符保证从左到右的求值;如果计算第二个操作数,则在第一个和第二个操作数的计算之间存在一个序列点。如果第一个操作数比较不等于 0,则不计算第二个操作数。
由于 C 没有真正的布尔类型,任何整数值(包括字符常量)都可以作为 || 的操作数。所以任何非零值都被认为是真,零被认为是假。另外,请注意上面第 4 段,该运算符具有“短路”评估,这意味着如果仅通过查看左侧就知道运算符的结果,则不会评估右侧。
现在让我们将其应用于您的表达式。第一:
if (str[i]=='u'||'o'||'i'||'e'||'a')
因为我们在这里处理多个运算符,所以我们需要应用详细的运算符优先规则here。由于相等比较运算符== 的优先级高于逻辑或运算符||,因此解析如下:
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
}
【讨论】:
不同的结果与运算符优先级有关。
x == y || z
与
相同(x == y) || z
与
不同x == (y || z)
您有表达式'u'||'o'||'i'||'e'||'a',所以在我们的例子中,y 将是'u',z 将是'o'||'i'||'e'||'a'。 z 将评估为真,因为至少有一个操作数(在本例中为所有操作数)非零。所以第一行将等价于(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。
【讨论】:
你需要:
if (str[i] == 'u' || str[i] == 'o' || str[i] == 'i' || str[i] == 'e' || str[i] == 'a' ) {/*...*/}
switch:
switch(str[i])
case 'u': case 'o': case 'i': case 'e': case 'a': {/*...*/}
可能有更好的机会为您提供更好的代码(自 C 的第一个版本以来,上述开关已用于有效的词法分析)并且很多人(包括我)也发现它更具可读性。 (如果您将案例保存在 {} 复合语句中,很多人会发现它更具可读性,但我正在经历一个尽可能将它们排除在外的阶段。)
【讨论】:
以下表达式总是返回true的原因:
if (str[i] == 'u'||'o'||'i'||'e'||'a')
是字符常量评估为真。所以,上面真的和这个是一样的:
if (str[i] == 'u'|| 1 || 1 || 1 || 1)
你打算这样做:
if (str[i] == 'u' || str[i] == 'o' || str[i] == 'i' || str[i] == 'e' || str[i] == 'a')
请注意,每次比较都需要重复相等表达式。
【讨论】:
if(strchr("uoiea", str[i])) 干净多了,对吧?
if ( str[i] >= 0 && strchr( “aeiou”, str[i] ) ) {...}。