CCFileUtils::getFileData疑惑

时间:2021-10-19 20:20:18

背景

这几天在使用cocos2d-x读取磁盘文件的时候,发现了CCFileUtils中一点不合理的地方,特此记录,以供研讨。

项目结构

①我使用的是cocos2d-x 2.1.3版本,CCFileUtils::getFileData的函数签名为:

CCFileUtils::getFileData疑惑

但据观测,即使是最新版的cocos2d-x 3.2,这部分声明和实现仍没有变化。

②我们假设在项目Resources目录下,有data.txt,其中内容大致为下图:

CCFileUtils::getFileData疑惑

③下面以win32和android两种平台做对比来分析其中出现的问题。

Win32

①读取文件的代码

CCFileUtils::getFileData疑惑

②实际执行的读取代码

文件路径:cocos2dx\platform\CCFileUtils.cpp

CCFileUtils::getFileData疑惑

③过程分析

在Windows中,行结尾符是”\r\n”,所以当程序执行完381行时,

*pSize = ( 5+2+6+2+6 , 21 );

当程序执行完384行时,

*pSize = ( 5+1+6+1+6 , 19 );

当程序返回时,

*pSize == 19; //没有问题

sizeof(pBuffer) == 21; //没有问题

pBuffer == “apple\norange\nbananba?????”; //问题很严重

④结果

当执行步骤①的代码时,我们使得data指向了这样一个字符串:

1.一段以”r”模式读取文件的代码,却在其中包含了’\n’;

2.这个字符串没有’\0’结尾。

这里的问题很好解决,只要我们添加下面一条语句,就可以避免悲剧:

data[pSize] = ‘\0’;

Windows平台上的问题很好解决,下面我们转向android.

Android

①读取文件的代码

CCFileUtils::getFileData疑惑

②实际执行的读取代码

文件路径:cocos2dx\platform\android\CCFileUtilsAndroid.cpp

CCFileUtils::getFileData疑惑

③过程分析

由于Android平台上行结尾符为\r,所以当执行完415行时,

*pSize = ( 5+1+6+1+6 , 19 );

sizeof(pBuffer) == 19

有问题吗?很严重。

④结果

下面回到步骤①,此时的data指向了一个字符串,这个字符串:

1.一段以”r”模式读取文件的代码,却在其中包含了’\n’;

2.这个字符串没有’\0’结尾。

但是,此时我们不能使用win32平台的解决方案。

data[size] = ‘\0’; //数组越界!

data[size-1] = ‘\0’;//banana中的最后一个a会被删除

此时,在语言层面上,只有把这段内存复制到一个更安全的地方,才可以解决这种冲突。如果你直接操纵data,几乎可以肯定会出问题。

错误解法:string str = (char*)data;

正确解法:string str( data, data+size );

结论

CCFileUtils::getFileData,一旦你以”r”模式来使用它,data就会变成一个没有’\0’结尾的字符串;一旦你在一个行结尾符是由单个字节组成的系统中使用它,你就不能给它加一个’\0’。如果不注意这两点,几乎可以肯定会误用这个函数。所以使用它的时候,请谨慎!

并且,请使用string str( data, data+size );的形式来复制这个易错字符串。

PS

话说这个函数的声明和实现相当容易被误用,不仔细分析几乎一定会被误用,因为其中的一些做法完全是和常识相违背的,不知道cocos2d-x的团队为什么不修改一下。