原文:Exercise 36: Safer Stringshtml
译者:飞龙git
我已经在练习26中,构建devpkg
的时候介绍了Better String库。这个练习让你从如今开始熟悉bstring
库,而且明白C风格字符串为何十分糟糕。以后你须要修改liblcthw
的代码来使用bstring
。程序员
当人们谈论C的问题时,“字符串”的概念永远是首要缺陷之一。你已经用过它们,而且我也谈论过它们的种种缺陷,可是对为何C字符串拥有缺陷,以及为何一直是这样没有明确的解释。我会试着如今作出解释,部分缘由是C风格字符串通过数十年的使用,有足够的证据代表它们是个很是糟糕的东西。github
对于给定的任何C风格字符串,都不可能验证它是否有效。vim
以'\0'
结尾的C字符串是有效的。安全
任何处理无效C字符串的循环都是无限的(或者形成缓冲区溢出)。数据结构
C字符串没有肯定的长度,因此检查它们的惟一方法就是遍历它来观察循环是否正确终止。函数
因此,不经过有限的循环就不可能验证C字符串。oop
这个逻辑很是简单。你不能编写一个循环来验证C字符串是否有效,由于无效的字符串致使循环永远不会中止。就是这样,惟一的解决方案就是包含大小。一旦你知道了大小,你能够避免无限循环问题。若是你观察练习27中我向你展现的两个函数:学习
译者注:检验C风格字符串是否有效等价于“停机问题”,这是一个很是著名的不可解问题。
void copy(char to[], char from[]) { int i = 0; // while loop will not end if from isn't '\0' terminated while((to[i] = from[i]) != '\0') { ++i; } } int safercopy(int from_len, char *from, int to_len, char *to) { int i = 0; int max = from_len > to_len - 1 ? to_len - 1 : from_len; // to_len must have at least 1 byte if(from_len < 0 || to_len <= 0) return -1; for(i = 0; i < max; i++) { to[i] = from[i]; } to[to_len - 1] = '\0'; return i; }
想象你想要向copy
函数添加检查来确保from
字符串有效。你该怎么作呢?你编写了一个循环来检查字符串是否已'\0'
结尾。哦,等一下,若是字符串不以'\0'
结尾,那它怎么让循环停下?不可能停下,因此无解。
不管你怎么作,你都不能在不知道字符串长度的状况下检查C字符串的有效性,这里safercopy
包含了程度。这个函数没有相同的问题,由于他的循环必定会停止,即便你传入了错误的大小,大小也是有限的。
译者注:可是问题来了,对于一个C字符串,你怎么获取其大小?你须要在这个函数以前调用
strlen
,又是一个无限循环问题。
因而,bstring
库所作的事情就是建立一个结构体,它老是包含字符串长度。因为这个长度对于bstring
来讲老是可访问的,它上面的全部操做都会更安全。循环是有限的,内容也是有效的,而且这个主要的缺陷也不存在了。BString库也带有大量所需的字串操做,好比分割、格式化、搜索,而且大多数都会正确并安全地执行。
bstring
中也可能有缺陷,可是通过这么长时间,可能性已经很低了。glibc
中也有缺陷,因此你让程序员怎么作才好呢?
有不少改进后的字符串库,可是我最喜欢bstrlib
,由于它只有一个程序集,而且具备大多数所需的字符串功能。你已经在使用它了,因此这个练习中你须要从Better String获取两个文件,bstrlib.c
和bstrlib.h
。
下面是我在liblcthw
项目目录里所作的事情:
$ mkdir bstrlib $ cd bstrlib/ $ unzip ~/Downloads/bstrlib-05122010.zip Archive: /Users/zedshaw/Downloads/bstrlib-05122010.zip ... $ ls bsafe.c bstraux.c bstrlib.h bstrwrap.h license.txt test.cpp bsafe.h bstraux.h bstrlib.txt cpptest.cpp porting.txt testaux.c bstest.c bstrlib.c bstrwrap.cpp gpl.txt security.txt $ mv bstrlib.h bstrlib.c ../src/lcthw/ $ cd ../ $ rm -rf bstrlib # make the edits $ vim src/lcthw/bstrlib.c $ make clean all ... $
在第14行你能够看到,我编辑了bstrlib.c
文件,来将它移动到新的位置,而且修复OSX上的bug。下面是差别:
25c25 < #include "bstrlib.h" --- > #include <lcthw/bstrlib.h> 2759c2759 < #ifdef __GNUC__ --- > #if defined(__GNUC__) && !defined(__APPLE__)
我把包含修改成<lcthw/bstrlib.h>
,而后修复2759行ifdef
的问题。
这个练习很短,只是让你准备好剩余的练习,它们会用到这个库。接下来两个联系中,我会使用bstrlib.c
来建立Hashmap`数据结构。
你如今应该阅读头文件和实现,以后编写tests/bstr_tests.c
来测试下列函数,来熟悉这个库:
bfromcstr
从C风格字符串中建立一个bstring
。
blk2bstr
与上面相同,可是能够提供缓冲区长度。
bstrcpy
复制bstring
。
bassign
将一个bstring
赋值为另外一个。
bassigncstr
将bsting
的内容设置为C字符串的内容。
bassignblk
将bsting
的内容设置为C字符串的内容,可是能够提供长度。
bdestroy
销毁bstring
。
bconcat
在一个bstring
末尾链接另外一个。
bstricmp
比较两个bstring
,返回值与strcmp
相同。
biseq
检查两个bstring
是否相等。
binstr
判断一个bstring
是否被包含于另外一个。
bfindreplace
在一个bstring
中寻找另外一个,而且将其替换为别的。
bsplit
将bstring
分割为bstrList
。
bformat
执行字符串格式化,十分便利。
blength
获取bstring
的长度。
bdata
获取bstring
的数据。
bchar
得到bstring
中的字符。
你的测试应该覆盖到全部这些操做,以及你从头文件中发现的更多有趣的东西。在valgrind
下运行测试,确保内存使用正确。