繁体   English   中英

C-在while循环内isdigit()调用后增加的奇怪行为

[英]C - Strange behavior with a post-increment on isdigit() call inside while loop

我有一个简单的程序,带有一个函数来检查C字符串是否只有整数,是否返回true (1)false (0)

#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>

bool isStrWholeNumber(const unsigned char *s) {
    if (*s == '\0') return false;

    while (isdigit(*s++));

    if (*s == '\0') return true;

    return false;
}

int main()
{
    unsigned char str[] = "a";
    bool b = isStrWholeNumber(str);

    printf("%d\n", b);

    return 0;
}

指针增量*s++应该在增量之前将值传递给isdigit函数,但是似乎它在增量之后传递值,因此它传递字符'\\ 0'而不是'a',因为该函数返回的是true。

更改函数以将指针增加到函数调用之外,可以正常工作,并为字符“ a”返回false:

bool isStrWholeNumber(const unsigned char *s) {
    if (*s == '\0') return false;

    while (isdigit(*s)) s++;

    if (*s == '\0') return true;

    return false;
}

为什么是while (isdigit(*s++)); 不工作吗?


结论

这是当您累了或睡不好时会发生的事情,最终您会犯此类错误。 如您在答案中所见,该程序运行正常。

经过一番休息后,我回到了这个功能,并取得了不错的效果,既快又小。 我使用了GCC分析器和gprof来测试性能,还检查了程序集的性能和代码大小。 在有和没有GCC优化-O3的情况下进行了测试。

这是带注释的结果,因此您可以理解:

 bool isStrWholeNumber(const unsigned char *s) {
    if (*s) {    // Check if string is not empty
        --s;     // Decrement pointer. It will be incremented in while bellow
        while (isdigit(*++s));    // Iterate over string until a non digit character is found
        return !*s;    // Returns 1 if end of string was reached, else, return 0
    }

    return false;    // Always returns false if string is empty
}

可能可以进一步优化此功能,但我不知道如何。

由于易读性差,许多不好的代码都考虑使用此功能,请不要在任何地方使用它。

程序运行正常。 在第一个代码中, isdigit(*s++)将返回0并且s将递增,并且if (*s == '\\0') return true;的语句if (*s == '\\0') return true; 将返回true
在第二个代码段中,将不评估*s++并且该语句return false; 将返回false

正如@haccks所解释的,该程序的行为符合预期。 无论isdigit的结果如何,您总是在递增s 这就是为什么您应该始终为清楚起见而不是为了简洁而写的原因。 对您的程序进行的微小修改使其可以执行您想要的操作,并使它更加清晰:

bool isStrWholeNumber(const unsigned char *s) {
   if (*s == '\0') return false;

   while (isdigit(*s))
       ++s;

   if (*s == '\0') return true;

   return false;
}

您的原始程序依赖于:

  1. ++运算符优先于*运算符,因此我们不会意外增加指针的内容。
  2. ++在增加指针之前返回该值,因此我们将获得正确的指针值。
  3. isdigit将获取取消引用的值。

在一行代码中发生了如此多的事情,它没有按您预期的那样表现是否令人惊讶?

顺便说一句,如果您没有得到正确终止的字符串,您的代码就是段访问冲突。

对于第一个版本,增量是无条件执行的。 因此,无论测试是否失败,指针都会递增,因此它始终指向下一个字符。 如果由于*s为NUL( '\\0' )而导致测试失败,则它仍将增加1。 if访问的字符超过了终止符,则以下内容将if访问,如果数组不够大,则该行为未定义的行为 但是,当您访问字符串的末尾时,绝对不是必需的

如果像第二个版本那样将增量放入循环主体中,则只有在测试为true while ,增量才会递增。 这就是想要的行为(据我从函数名称中看到的那样)。

注意:您无需取消引用指针即可递增。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM