C - 在 while 循环内对 isdigit() 调用进行 post 增量的奇怪行为

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

我有一个简单的程序,它有一个函数来检查 C 字符串是否只有整数,如果有 returns true (1)false (0) :

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

bool isStrWholeNumber(const unsigned char *s) {
    if (*s == '[=11=]') return false;

    while (isdigit(*s++));

    if (*s == '[=11=]') return true;

    return false;
}

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

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

    return 0;
}

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

更改函数以在函数调用之外递增指针,有效,对字符 'a':

返回 false
bool isStrWholeNumber(const unsigned char *s) {
    if (*s == '[=12=]') return false;

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

    if (*s == '[=12=]') 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
}

也许这个功能可以进一步优化,但我不知道如何。

这个函数被很多糟糕的代码认为是因为易读性差,不要到处使用它。

对于第一个版本,增量是无条件执行的。因此,无论测试是否失败,指针都会递增,因此它始终指向下一个字符。如果因为*s为NUL('[=11=]')而测试失败,仍然加1。后面的if访问字符过去结束符,如果数组不够大,则为 未定义行为。但是,当您访问超过 sting.

的末尾时,它绝对是不需要的

如果像第二个版本一样将增量放入循环体中,它只会增加 while 测试为真。这就是想要的行为(据我从函数名称中看到的)。

注意:您不需要取消引用指针来递增。

程序运行正常。在第一个代码中,isdigit(*s++) 将 return 0s 将递增,语句 if (*s == '[=13=]') return true; 将 return true.
在第二个片段中,*s++ 将不会被评估,语句 return false; 将 return false.

正如@haccks 所解释的那样,该程序的行为符合预期。无论 isdigit 的结果如何,您总是递增 s。 这就是为什么你应该始终为了清​​晰而不是为了简洁而写作的原因。 对您的程序稍作修改,使其可以按照您的意愿进行操作,并使其更加清晰:

bool isStrWholeNumber(const unsigned char *s) {
   if (*s == '[=10=]') return false;

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

   if (*s == '[=10=]') return true;

   return false;
}

您的原程序依赖于:

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

一行代码中发生了这么多事情,它没有像您预期的那样运行是否令人惊讶?

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