前几天安排部门里一位年轻的小伙伴写一个工具给我们内部使用,其中要求他实现一个功能就是输入一个文件名,然后根据输入的文件名字符串查找相关文件。那么这里肯定要入参检测,看看字符串长度有没有问题,于是小伙子噼里啪啦写了类似下面这段代码:
#include<stdio.h>
#include<string.h>
int main()
{
const char * test_str="test_str";
if(-1 < strlen(test_str))
printf("字符串长度正常.\n");
else
printf("字符串长度异常.\n");
return 0;
}
写完了之后当然是编译运行此代码。
于是在ubuntu下使用gcc编译器编译出a.out文件,编译器没有报任何错误或者警告,
接着./a.out开始运行程序。运行结果可以看到程序走的是else分支,报字符串异常。
这时候小伙子开始思考问题,奇怪了,test_str这个字符串不是长度绝对大于0,大于-1的,怎么会这样?好吧,那就在if判断前面加一行打印,看看strlen返回的字符串长度是多大。
添加printf打印长度修改代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
const char * test_str="test_str";
printf("str len:%u\n",strlen(test_str));
if(-1 < strlen(test_str))
printf("字符串长度正常.\n");
else
printf("字符串长度异常.\n");
return 0;
}
然后重新编译运行修改后代码,可以看到结果如下:
可以看到strlen返回的长度值就是8,没有问题,其值是大于-1的,一切正常。但是任然是走的else分支,也就是if判断条件-1 < strlen(test_str) 结果为False,但是不应该啊,8明明大于-1。
小伙伴想了半天,抓破脑袋也不知道是什么情况?不知道问题原因在哪里。
于是就叫来了我帮忙看看。我告诉他,既然-1 < strlen(test_str) 结果为False,那么肯定是这个比较有问题,我没有直接告诉他答案,还是让他仔细再看看。小伙伴子想了半天还是告诉我不知道。
于是开始了我带着他查找的过程。
我问他,strlen返回是什么类型啊,他去查找了一下告诉我说,是size_t类型。
那具体是什么类型,他告诉我说是unsigend int类型,也就是无符号整形类型。
于是我们他,你现在知道问题在哪里了吗?他想了半天还是回一句,还是不知道哦。
于是我继续问,那请问-1是什么类型,他答到,是signed int,有符号整形。
所以你现在知道问题在哪里了吧?
具体来说,strlen 返回的是 size_t,这是一个无符号整数类型。比较有符号整数和无符号整数的时候,根据C语言隐式类型转换的原理,如果是int型与uint型进行比较,则会将int型数据转换为uint型,则-1变成了 2^32-1 = 4294967295,由于-1 被转换为一个非常大的正整数,这将导致 -1 永远都小于 strlen 返回值,即使 strlen 返回的是0。
是不是这样,下面我们来试一下吧。把strlen前面加个强制类型转换,转成int类型,再看看结果怎么样?
小伙子噼里啪啦代码改成如下:
#include<stdio.h>
#include<string.h>
int main()
{
const char * test_str="test_str";
printf("str len:%u\n",strlen(test_str));
if(-1 < (int)strlen(test_str))
printf("字符串长度正常.\n");
else
printf("字符串长度异常.\n");
return 0;
}
下面运行修改后代码,可以看到结果如下:
可以看到,“字符串长度”正常出来了,表示if判断结果为真,通过了。
基础不牢,地动山摇。
总结一下,这里问题就是C语言的隐式类型转换,加上关键是这里被比较的数是-1,是负数,强制类型转后之后就变成一个很大的正数。如果你这里-1换成0,绝对没有问题的,强制转换也没有问题,这个跟负数的储存方式有关系了。所以这里这个问题非常隐晦,基础不牢,还是真的不容易看出问题所在。
后续持续更新系列高质量文章,码字不易,觉得写的不错欢迎关注、点赞、收藏以及提问。