【问题标题】:Using NULL as a terminator in arrays?在数组中使用 NULL 作为终止符?
【发布时间】:2010-12-02 18:24:23
【问题描述】:

出于学习目的,我喜欢“重新发明轮子”,因此我正在开发一个用于字符串的容器类。使用NULL 字符作为数组终止符(即数组中的最后一个值将是NULL)会干扰以空值结尾的字符串吗?

我认为只有添加空字符串才会有问题,但我可能会遗漏一些东西。

编辑:这是用 C++ 编写的。

【问题讨论】:

  • “干扰”是什么意思。您可以发布任何代码来显示您所描述的内容吗?
  • 这是 C 还是 C++?您存储的字符串是什么类型的?
  • @Sht C++。对不起,我没有说清楚。 @SpeksETC 我的意思是,由于字符串是空终止的(我认为?),一个空字符串将具有与“数组结尾”值相同的值。
  • 您不能在字符串中嵌入空值,这对于非 ASCII 字符集和使用字符串处理二进制协议等很有用。
  • 你说的是双NULL终止的字符串吗?哪里有一组以 NULL 结尾的字符串,所有字符串都与 NULL 字符分隔符连接,最后还有一个额外的 NULL 来终止整个数组?你想看看这个:blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx

标签: c++ arrays string null containers


【解决方案1】:

"" 是 C 和 C++ 中的空字符串,而不是 NULL。请注意,"" 只有一个元素(而不是零),这意味着它相当于 {'\0'} 作为char 的数组。

char const *notastring = NULL;
char const *emptystring = "";

emptystring[0] == '\0';  // true
notastring[0] == '\0';   // crashes

【讨论】:

  • 但是字符串是以null结尾的,所以空字符串的值不会是"" + NULL,因此只有NULL的值吗?
  • NULL 可能与 NUL 的值相同,也可能不同。
  • 没有"" + NULL 这样的东西。那是添加一个数组和一个指针(它可能有效,但没有意义)。因为""只有一个元素,所以它的地址不能是NULL;该元素必须存储在某个地方。
  • 有趣的是,"" + NULL 可能意味着什么,因为NULL 不能保证为 0,但它肯定不会意味着任何你想要的。
【解决方案2】:

不,不会,因为您不会存储在 char 数组中,而是将存储在 char* 数组中。

char const* strings[] = {
  "WTF"
, "Am"
, "I"
, "Using"
, "Char"
, "Arrays?!"
, 0
};

【讨论】:

    【解决方案3】:

    这取决于您存储的字符串类型。

    如果您要存储 C 风格的字符串,它们基本上只是指向字符数组 (char*) 的指针,那么 NULL 指针值和空字符串之间是有区别的。前者表示指针为“空”,后者表示指针指向一个数组,该数组包含一个字符值为 0 ('\0') 的单个项目。所以指针仍然有一个值,测试它 (if (foo[3])) 将按预期工作。

    如果您存储的是 string 类型的 C++ 标准库字符串,则没有 NULL 值。那是因为没有指针,string 类型被视为单个值。 (而指针在技术上不是,但可以看作是一个参考。)

    【讨论】:

      【解决方案4】:

      我认为你很困惑。虽然 C 字符串是“空终止”的,但没有“空”字符。 NULL 是空 指针 的名称。 C 字符串的终止符是一个空 字符,即一个值为 0 的字节。在 ASCII 中,这个字节(有点令人困惑)命名为 NUL

      假设您的类包含一个char 数组,用于存储字符串数据。您不需要“标记数组的结尾”;该数组具有在编译时设置的特定大小。您确实需要知道实际使用了多少空间;字符串数据上的空终止符为您完成了这项工作 - 但您可以通过实际记住长度来获得更好的性能。此外,具有静态大小的字符缓冲区的“字符串”类根本不是很有用,因为缓冲区大小是您可以拥有的字符串长度的上限。

      所以一个更好的字符串类应该包含一个char* 类型的指针,它指向一个动态分配的(通过new[]char 数组。同样,“标记数组的结尾”是没有意义的,但是您需要记住字符串的长度(即正在使用的空间量)和分配的大小(即可以在重新分配之前使用)。

      【讨论】:

      • 确实,我很困惑。谢谢你的澄清。
      【解决方案5】:

      当您从std::string 复制时,使用迭代器begin()end() 并且您不必担心NULL - 实际上,NULL 只有在您调用c_str() 时才会出现(在在这种情况下, this 指向的内存块将有一个 NULL 来终止字符串。)如果你想 memcpy 使用 data() 方法。

      【讨论】:

        【解决方案6】:

        您为什么不遵循vector 使用的模式 - 将元素的数量存储在您的容器类中,然后您总是知道其中有多少值:

        vector<string> myVector;
        
        size_t elements(myVector.size());
        

        使用x 实例化字符串,其中const char* x = 0; 可能会出现问题。请参阅执行此操作时调用的 Visual C++ STL 中的以下代码:

        _Myt& assign(const _Elem *_Ptr)
            {   // assign [_Ptr, <null>)
            _DEBUG_POINTER(_Ptr);
            return (assign(_Ptr, _Traits::length(_Ptr)));
            }
        
        static size_t __CLRCALL_OR_CDECL length(const _Elem *_First)
            {   // find length of null-terminated string
            return (_CSTD strlen(_First));
            }
        

        【讨论】:

        • 这并不妨碍您跟踪当前的有效元素计数。 vector 也在后台使用连续(即数组)存储。
        • OP 没有使用向量,因为它会与为了学习目的而重新发明轮子的声明的偏好相冲突。
        • @Thomas - 我不是说使用vector,我是说设计容器类,这样就不需要人为的“终结者”构造来跟踪其元素计数。
        • @Steve Ahh 我明白了。 Maxpm,作为您学习的一部分,请考虑 std::vector 的方式。
        • @Thomas - 我仍在检查,但几乎可以肯定 string str(0); 是 Visual C++ 上的一个严重问题。需要“最后一个元素”标记的容器类将很难概括,这应该是 OP 的下一个项目 imo。
        【解决方案7】:
        #include "Maxmp_crafts_fine_wheels.h"
        MaxpmContaner maxpm;
        maxpm.add("Hello");
        maxpm.add(""); // uh oh, adding an empty string; should I worry?
        maxpm.add(0);
        

        在这一点上,作为 MaxpmContainer 的用户,还没有阅读过您的文档,我希望如下:

        strcmp(maxpm[0],"Hello") == 0;
        *maxpm[1] == 0;
        maxpm[2] == 0;
        

        通过“将其解释为内存地址”运算符 * 避免了位置 2 处的零终止符和位置 1 处的空字符串之间的干扰。位置一不会为零;它将是一个整数,如果将其解释为内存地址,则结果为零。位置二将为零,如果您将其解释为内存地址,这将是您程序的突然无序退出。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多