【问题标题】:What does `(c = *str) != 0` mean?`(c = *str) != 0` 是什么意思?
【发布时间】:2017-03-29 10:41:27
【问题描述】:
int equiv (char, char);
int nmatches(char *str, char comp) {
    char c;
    int n=0;
    while ((c = *str) != 0) {  
       if (equiv(c,comp) != 0) n++;
      str++;
    }
    return (n);    
}

“(c = *str) != 0”实际上是什么意思? 有人可以向我解释一下或帮助我自己搜索解释的正确术语吗?

【问题讨论】:

  • 这会取消引用指针str 并将其指向的值分配给c,然后将c 与零进行比较。
  • *str 取值来自str 的地址; c = *str 将此值分配给c(c = *str) != 0 检查此值是否不是 NULLNULL 是 C/C++ 中字符串的结束标记,参见“空终止字符串”en.wikipedia.org/wiki/Null-terminated_string
  • 另外你可能不知道赋值是有价值的。例如,可以在 C/C++ 中编写 (a=(b=c)) == d。这里的每个赋值不仅仅是从右到左赋值,而是在括号外返回。
  • @Dims NULL 用于描述空指针,而不是空终止。混合这些会导致混乱。另外,请不要在 cmets 中回答。
  • @Dims 它是一个以空字符结尾的字符串,而不是一个以空字符结尾的字符串 - 该字符是 NUL 而不是 NULL。最近我自己也犯了这个错误:/

标签: c++ c pointers syntax


【解决方案1】:

这个表达式有两部分:

  • c = *str - 这是对 c 的简单赋值,来自解除对指针的引用,
  • val != 0 - 这是与零的比较。

这是可行的,因为赋值是一个表达式,即它有一个值。赋值的值与被赋值的值相同,在这种情况下,指针指向的char。所以基本上,你有一个循环,它跟踪一个以 null 结尾的字符串,将每个单独的 char 分配给 c

请注意,!= 0 部分在 C 中是多余的,因为 while 循环的控制表达式隐含地与零进行比较:

while ((c = *str)) {
    ...
}

从语法的角度来看,第二对括号是可选的,但它保留在这样的赋值中,以表明赋值是有意的。换句话说,它告诉读者你的代码你真的打算写一个赋值c = *str,而不是比较c == *str,这在循环控制块内部更常见。第二对括号也禁止编译器警告。

【讨论】:

  • != 0 是多余的,但是这个答案替代额外的 () 多余的也是如此。两者都有相同的目的:消除编译器警告并清楚说明代码不是c == *str。根据风格和经验,一个比另一个更受欢迎。
【解决方案2】:

令人困惑,

while ((c = *str) != 0) {

是更容易阅读的重言式

while (c = *str) {

这也有将*str处的字符赋值给c的效果,一旦*str\0,循环就会终止;即当到达字符串的末尾时。

上述条件中的赋值乍一看可能会让人感到困惑,(参见完全不同的c == *str 的行为),但它们是 C 和 C++ 中非常有用的部分,你需要习惯它们.

【讨论】:

  • 我认为你认为一个等同于另一个(即它们的等价是重言式)。
  • 我不确定是否将作业称为“副作用”。
  • 首选while ((c = *str)),以抑制任何关于错误==的潜在警告。
  • @Bathsheba:没有括号,clang 会给出警告“使用赋值结果作为没有括号的条件”。为了抑制它,您必须添加额外的括号或传递-Wno-parentheses,其中我选择前者。
  • @Bathsheba:但复杂性不是美德。 (Funny opening by Herb Sutter。)我更喜欢第一个变体,我什至会写while ((c = *str) != '\0') 以表明我正在寻找空终止符。 while (c = *str) 行是正确的,但我想知道你是否真的指的是 == 那里。添加的括号就像“原文如此”,这是一件好事。写 strcpy(char *d, char *s) { while (*d++ = *s++); } 可能很酷,但我不想在我的代码中包含它。
【解决方案3】:

(c = *str) 是一个表达式,它本身就有一个值。它是一个赋值,赋值的值就是被赋值的值。所以(c = *str)的值就是*str的值。

代码基本检查,刚刚分配给c*str的值是否不是0。如果不是,那么它将使用该值调用函数equiv

一旦分配了0,这就是字符串的结尾。该函数必须停止从内存中读取数据。

【讨论】:

    【解决方案4】:

    它遍历字符串str 中的每个字符,将它们分配给c,然后查看c 是否等于0,这表示字符串的结尾。

    虽然代码确实应该使用'\0',因为这更明显是一个 NUL 字符。

    【讨论】:

      【解决方案5】:

      我们在 while 循环中遍历 str 并提取其中的每个 char 符号,直到它等于 0 - char 字符串结尾的主要规则。

      这是 'for' 循环等效项:

       for (int i = 0; i < strlen(str); ++i )
             std::cout << str[i];
      

      【讨论】:

      • ...直到它不等于零...”应该读作“...直到它等于零...” i>”不应该吗?
      • 对。从我的语言直译的后果:)
      • 将 strlen 作为条件检查会冒着变成 O(N * N) 的风险。编译器可能会优化,但我不会依赖它。
      【解决方案6】:

      这只是草率编写的代码。目的是将字符串str中的一个字符复制到c中,然后检查它是否为空终止符。

      在 C 中检查空终止符的惯用方法是显式检查 '\0'

      if(c != '\0')
      

      这是所谓的自记录代码,因为在 C 中编写空终止符的事实上的标准方法是使用八进制转义序列 \0


      另一个错误是在条件中使用赋值。这在 1980 年代被认为是不好的做法,从那时起,每个编译器都会对此类代码发出警告,“可能不正确的分配”或类似的。这是不好的做法,因为赋值包含副作用,并且具有副作用的表达式应尽可能简单。但这也是不好的做法,因为很容易混淆===


      代码可以很容易地重写为更易读和更安全的代码:

      c = *str;
      while (c != '\0')
      {
        if(equiv(c, comp) != 0)
        {
          n++;
        }
        str++;
        c = *str;
      }
      

      【讨论】:

        【解决方案7】:

        你不需要char c,因为你已经有了指针char *str,你也可以用!= '\0'替换!= 0以获得更好的可读性(如果不兼容)

        while (*str != '\0')
        {  
            if (equiv((*str),comp)
                  != 0)
            { n++; }
        
            str++;   
        }
        

        要了解代码的作用,可以这样阅读

        while ( <str> pointed-to value is-not <end_of_string> )
        {
            if (function <equiv> with parameters( <str> pointed-to value, <comp> )
                    returned non-zero integer value)
            then { increment <n> by 1 }
        
            increment pointer <str> by 1 x sizeof(char) so it points to next adjacent char
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-01
          • 1970-01-01
          • 2019-12-08
          • 2015-03-20
          • 2020-01-07
          • 1970-01-01
          • 2018-09-10
          相关资源
          最近更新 更多