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 0
和 s
将递增,语句 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;
}
您的原程序依赖于:
++
运算符优先于 *
运算符,因此我们不会意外增加指针的内容。
++
returns 增加指针之前的值,以便我们获得正确的指针值。
isdigit
将获得取消引用的值。
一行代码中发生了这么多事情,它没有像您预期的那样运行是否令人惊讶?
顺便说一句,如果您没有获得正确终止的字符串,您的代码就会发生段访问冲突。
我有一个简单的程序,它有一个函数来检查 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':
返回 falsebool 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 0
和 s
将递增,语句 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;
}
您的原程序依赖于:
++
运算符优先于*
运算符,因此我们不会意外增加指针的内容。++
returns 增加指针之前的值,以便我们获得正确的指针值。isdigit
将获得取消引用的值。
一行代码中发生了这么多事情,它没有像您预期的那样运行是否令人惊讶?
顺便说一句,如果您没有获得正确终止的字符串,您的代码就会发生段访问冲突。