【问题标题】:How to check if variable equal to multiple values如何检查变量是否等于多个值
【发布时间】: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


    【解决方案1】:

    || 运算符不允许您以这种方式“链接”条件。 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>(因此每个可能的对象表示都是有效的,并且具有 不同的值)。

    但无论如何我们都会把它留下来,只是为了保持理智。

    【讨论】:

      【解决方案2】:

      || 运算符与(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
      }
      

      【讨论】:

        【解决方案3】:

        不同的结果与运算符优先级有关。

        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

        【讨论】:

          【解决方案4】:

          你需要:

          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 的第一个版本以来,上述开关已用于有效的词法分析)并且很多人(包括我)也发现它更具可读性。 (如果您将案例保存在 {} 复合语句中,很多人会发现它更具可读性,但我正在经历一个尽可能将它们排除在外的阶段。)

          【讨论】:

            【解决方案5】:

            以下表达式总是返回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')
            

            请注意,每次比较都需要重复相等表达式。

            【讨论】:

            • 为什么 (str[i]==('u'||'o'||'i'||'e'||'a')) 是假的?
            • 右手边总是计算为 1,并且(我猜)str[i] 不等于 1。
            • 在没有完全分隔的情况下没有任何编码方式吗?又名 - if (str[i] == 'u' || str[i] == 'o' || str[i] == 'i' || str[i] == 'e' || str[ i] == 'a')
            • if(strchr("uoiea", str[i])) 干净多了,对吧?
            • @chux: 那么if ( str[i] &gt;= 0 &amp;&amp; strchr( “aeiou”, str[i] ) ) {...}
            猜你喜欢
            • 2011-11-20
            • 2021-03-28
            • 2023-01-19
            • 1970-01-01
            • 1970-01-01
            • 2018-10-06
            • 1970-01-01
            • 2012-08-25
            相关资源
            最近更新 更多