我是一个 C++初学者,提一个比较简单的问题,希望可以得到诸位的解答。谢谢!
众所周知,在调用 std::string 的 c_str()函数时,返回的是一个 const char*类型,会以\0 结束。
在我自己实现 stl 时,我发现结尾的\0 会给 insert 等函数带来很多麻烦,所以存储字符串时,根本没有用\0 结束。但是,这样的问题就是,c_str()的返回值,经常会超出范围。
所以我想问问大家,在你们实现 stl 的时候,是以\0 为结尾使结果比较好看(但是这样会多占用一个字节),还是从 allocator 方面使他的结尾必定为 0,或者还有其他的解决办法?
一个新手比较困惑的问题,再次感谢!
1
inhzus OP 我有参考 GITHUB 上邹晓航的 stl 实现,他的实现同样没有在结尾添加\0 (在我看来,在结尾添加\\0 实在太不具有美感了),可能是通过 allocator 的实现,使得存储空间的结尾大部分情况下为\0。
或者是不是 c_str()这个函数,作为设计者根本不需要考虑这个结尾符号,使用过程中用 size_t n 加以限制就可以了? |
2
soli 2018-02-08 12:36:13 +08:00 1
如果我来实现的话,我会在 `c_str()` 里在字符串结尾加个 `\0` 再返回,而其他函数只以 `len` 为准。
另外,在判断是否需要重新分配内存时,考虑多预留一个字节。 另外,新版标准的 `data()` 函数应该等同 `c_str()` 了。所以,`data()` 返回的数据也是以 `\0` 结尾。 |
3
skadi 2018-02-08 12:51:03 +08:00
把最后一个 char 设置为 0,是历史问题.在 c 中表示为空.
|
4
hitmanx 2018-02-08 14:10:32 +08:00
用户拿到 c_str()肯定是希望它是 null-terminated 的,比如直接 printf 什么的...看了下 cppreference,也是同样的意思(可以看我高亮的那部分).不知道是不是通过你说 zero memory(memset)未用的 buffer 的方式实现的.STL 源码都不太好读,string 什么的本质就是一个 buffer 的管理,我记得有例如比如 copy-on-write 之类的优化在里面
http://en.cppreference.com/w/cpp/string/basic_string/c_str Returns a pointer to a **null-terminated** character array with data equivalent to those stored in the string. The pointer is such that the range [c_str(); c_str() + size()] is valid and the values in it correspond to the values stored in the string **with an additional null character after the last position**. |
5
fcten 2018-02-08 14:41:17 +08:00
c_str 是一个 c 风格的兼容函数。一般是在调用 c 库的时候会用到。所以返回时必须要以 '\0' 结尾
比较方便的做法是,申请内存的时候,总是多申请一些。确保 buffer 的长度始终大于 size。这样只要在 c_str 中简单地在字符串后面写入一个 '\0' 再返回就可以了。 多申请一些内存并不会太浪费。越长的字符串浪费的比率越小。而短字符串往往经常进行拼接操作。预留 buffer 空间既可以减少内存分配次数,也可以减少内存碎片。 |
6
gnaggnoyil 2018-02-08 14:43:44 +08:00 2
解决办法就是承认面对现实的需求不存在一个一劳永逸的方法可以在任何情况下都方便并且高效没有 overhead 地表示一个字符串.LZ 也可能感觉到了 null-terminated 表示法的局限,并且觉得是否有什么可行的替代品.但这里的问题是对于一个 const char *由于历史和与 C 交互的原因在大部分场景里它所被期望的都是一个 null-terminated string 而不是别的什么东西.要想摒弃 null terminated 的假设那么就需要引入新的设施,这也是为什么会有 range ts,和 C++17 的 string_view 的原因.
当然这里是假设 LZ 在自己制造一个 string 类库.如果想实现标准库的 string,那么就乖乖遵照标准的接口要求,把\0 加上吧…… |
7
MeteorCat 2018-02-08 15:07:27 +08:00 via Android
一般 string 都是带\0 的实现,如果楼主想自己实现一个非\0 结尾的 string 可以参考陈硕大神 muduo/base/LogStream 实现,里面有个 FixBuffer 是直接非\0 实现,不过要自己分配缓存长度
|
8
wangxn 2018-02-08 18:46:35 +08:00 via Android
你预分配多点空间和加一个 0 有啥区别?
|
9
owt5008137 2018-02-09 09:48:45 +08:00 via Android
std::string 的末尾补 0 只是为了方便兼容一些 c 库。补的 0 又不算在 size 里没人逼你用啊,你要不开心就当它不存在好了。
|
10
linux40 2018-02-09 10:30:08 +08:00 via Android
你不以 0 结尾,自己写个带锁的 operator<<(std::basic_ostream&, const string &)么。
|
11
GeruzoniAnsasu 2018-02-09 11:05:15 +08:00
标准里就没规定 string 必须\0 结尾
std 的 string 就是跟结尾 0 无关的,可以当做普通 buffer 来用(甚至还加了 data()成员函数) 而且不规定 string 必须 const c_str 只是为了与 c 兼容 没见有 reserve 和 capacity 嘛,在[size]处预留个 0 呗 |
13
inhzus OP @gnaggnoyil 谢谢您。我想了想要实现完全和 string 相同只能这样有点麻烦了= =
|
14
inhzus OP @hitmanx 我和您想的一样的,一开始打算确实是通过未用的 buffer 来使结尾为 0,不过那样确实还不如多占用一个字节的空间。
|
15
lsmgeb89 2018-02-13 10:45:35 +08:00
看了下邹晓航的项目,都好硬啊,NB
|