【问题标题】:How to determine the size of an array of strings in C++?如何确定 C++ 中字符串数组的大小?
【发布时间】:2010-02-16 04:51:58
【问题描述】:

我试图简单地打印出数组中包含的值。

我有一个名为“结果”的字符串数组。我不知道它到底有多大,因为它是自动生成的。

根据我的阅读,您可以通过以下方式确定数组的大小:

sizeof(result)/sizeof(result[0])

这是正确的吗?因为对于我的程序,sizeof(result) = 16 和 sizeof(result[0]) = 16 所以代码会告诉我我的数组大小为 1。

但是这看起来不正确,因为如果我手动打印出这样的数组值:

std::cout << result[0] << "\n";
std::cout << result[1] << "\n";
std::cout << result[2] << "\n";
std::cout << result[3] << "\n";
etc...

...然后我看到了我正在寻找的结果值。该数组的长度/大小超过 100 个。

似乎确定数组的大小/长度应该非常简单......所以希望我只是在这里遗漏了一些东西。

我有点 C++ 新手,所以任何帮助将不胜感激。

【问题讨论】:

  • 我将 sizeof(result)/sizeof(result[0]) 用于原始类型并且它曾经可以工作,但是如果您使用的是字符串,为什么还要使用数组,请使用向量.
  • 请出示result的声明。没有它,就不可能说出你的问题是什么。
  • @David Thornley:我认为可以肯定地假设他不是在处理一个包含一个元素的真实数组,唯一的另一种方法是他可以在两个sizeof 调用中得到 16。
  • @HansPassant 让我们明智地假设字符串存储在char 的数组中(例如char result[][16] = { "maximum", "of", "fifteen", "characters", "per", "string" };)......有什么证据表明机器是128 位的? 128 位甚至意味着什么?是公交车的宽度吗?如果是这样,则有 64 位机器没有 64 位总线,而 32 位机器有。是原生类型的宽度吗?如果是这样,即使是 1995 年的 Pentium Pros 也有一些 128 位寄存器......

标签: c++ arrays string sizeof cout


【解决方案1】:

您无法在 C++ 中动态确定数组的大小。您必须将大小作为参数传递。

附带说明一下,使用标准库容器(例如矢量)可以缓解这种情况。

在您的 sizeof 示例中,sizeof(result) 要求指针的大小(可能是 std::string)。这是因为当传递给函数时,实际的数组类型“衰减”为指向元素的指针类型(即使函数被声明为采用数组类型)。 sizeof(result[0]) 返回数组中第一个元素的大小,巧合的是,它也是 16 个字节。在您的平台上,指针似乎是 16 字节(128 位)。

请记住,在 C++ 中,sizeof总是在编译时评估,而不是在运行时评估。

【讨论】:

    【解决方案2】:

    作为旁注,有更好的方法来检查数组的大小(对于数组在范围内并且没有衰减为指针的情况),它们是类型安全的:

    // simple: runtime result
    template <typename T, std::size_t N>
    inline std::size_t sizeof_array( T (&)[N] ) {
       return N;
    }
    
    // complex: compile time constant
    template <typename T, std::size_t N>
    char (&static_sizeof_array( T(&)[N] ))[N];   // declared, not defined
    #defined SIZEOF_ARRAY( x ) sizeof(static_sizeof_array(x))
    

    在这两种情况下,编译器都会检测您是否尝试传入指针(动态数组或衰减数组):

    void f( int array[] ) { // really: void f( int *array )
    {
    //   sizeof_array(array);              // compile time error
    //   int another[SIZEOF_ARRAY(array)]; // compile time error
    }
    int main() {
       int array[] = { 1, 2, 3 };
       std::cout << sizeof_array(array) << std::endl; // prints 3
       int another_array[ SIZEOF_ARRAY(array) ];
       std::cout << sizeof_array(another_array) << std::endl; // 3 again
    }
    

    【讨论】:

    • 这个答案很重要。与 C 技巧不同,这个技巧将拒绝在指针上工作,并且您永远不会以 1 结束。只接受数组。如果您没有数组可以提供给宏,那么这意味着您必须手动跟踪初始数组的大小。
    • 非常好!我不知道你能做到这一点。在这个问题之前,我什至不知道对数组的引用类型。 Bjarne 在圣经中绝对没有提到它。
    • 这都是提供数组中元素数量的通用解决方案,而不是它在内存中占用的大小。使用sizeof 计算元素数(sizeof(array)/sizeof(array[0]))比这些解决方案更糟糕,因为用户代码可能错误地传递一个指针(或任何其他定义了operator[] 的对象采用整数)并且代码将静默编译并提供错误的结果。 sizeof(array)/sizeof(array[0]) 相对于第一个变体的优势在于它提供了编译时间常数,但这并不是第二个(更复杂的)变体的优势。
    • 我在不同的地方见过它,我用它来避免计算初始化列表中的元素。请注意,它与 OP 中的 sizeof(array)/sizeof(array[0]) 具有完全相同的用例,如果您在 S.O. 中搜索。和其他编程论坛,你会发现有很多关于数组大小的问题/答案。
    • 我从不为堆栈分配的初始化常量数组的大小手动维护一个显式常量。这不是 DRY。相反,我定义了我的数组,并使用“第二个”技巧来获得元素的确切数量。如果我需要将此数组与另一个常量匹配(例如,来自枚举),那么我使用 STATIC_ASSERT(参见 loki、boost、...)来检查两个大小(数组的隐式大小和枚举最大值)匹配。
    【解决方案3】:

    如果您拥有的是一个“真实”数组,那么 sizeof(x)/sizeof(x[0]) 技巧有效。然而,如果你拥有的是一个 pointer (例如从函数返回的东西),那么这个技巧就行不通了——你最终将指针的大小除以 a 的 sizeof指针。它们指向不同类型的指针,但在典型系统上,所有指针的大小都相同,因此您会得到一个。即使指针大小不同,结果仍然与你有多少字符串无关。

    【讨论】:

    • 它们是如何指向不同类型的?数组必须是同质的,不是吗?什么是“真实”数组?如果 sizeof 在创建堆栈分配数组的同一范围内使用,它是否真的返回整个数组大小?
    • 所以经过进一步研究,sizeof(x) 将返回x 的总大小,因为x 是在同一函数中定义的堆栈分配数组。但是在这种情况下,您已经有一个用于声明数组大小的编译时常量,因此您只需要知道元素类型的大小即可计算数组的大小。一旦“数组”被传递给一个函数(即使使用正式的数组参数声明),该数组就被称为“衰减”为一个指针。显然编译器无法知道在运行时传递的实际数组的大小。
    • 您可以通过使用对数组的引用来避免衰减为指针。此外,即使您可以访问当前范围内的数组和元素大小,为什么总是显式地使用它并使代码更改更加困难?
    • @gf:这如何使您的代码更难更改(假设您使用的是 const 而不是文字)?当使用对数组的引用时,您必须在编译时提供数组大小。请注意,为了更正我之前的评论,您不能声明正式的数组参数,只能声明指向元素的指针或对数组类型的引用。
    • @StingRaySc:当您使用未命名的常量时,您必须在更改它时到处替换它,当您更改命名常量的名称时也是如此。此外,对模板函数使用对数组参数的引用,您不必显式提供数组大小。
    【解决方案4】:

    最好使用std::vector&lt;std::string&gt; 而不是原始数组。那么你就不必手动管理数组内存,如果你想知道元素的数量,你可以使用size()方法。

    如果您使用动态分配的原始数组,您需要自己跟踪其大小,则无法从数组中获取大小。最好将其保存在一个额外的变量中。

    【讨论】:

    • 中肯的建议,但没有回答问题。
    【解决方案5】:

    sizeof(array)/sizeof(element) 适用于定长数组的定长数组(不是指针)。 作为字符串数组,我们最常使用指向各种(固定)长度字符串的指针(固定长度)数组,因此这个技巧不起作用。 sizeof() 用于在编译时知道大小的对象。它不适用于动态分配的数据本身。

    当一个对象包含指针时,例如字符串数组, sizeof() 返回最高级别(固定大小)结构的大小。通常它只是单个指针的大小。它不包括指针指向的已分配数据的大小。因为该数据实际上不是主要对象的一部分,它确实是一个或多个单独的对象(我们这里有聚合而不是组合,请参阅http://en.wikipedia.org/wiki/Object_composition)。

    在 C++ 中,使用向量非常方便满足您的需求。也可以使用其他合适的标准容器。 length() 和 size() 方法是同义词,见http://www.cplusplus.com/reference/string/string/size/)

    附:请注意,对于 std::string 对象,sizeof(s) 是一个常数,与 s.length() 返回的实际(可变)字符串长度无关。实际分配的内存大小由 s.capacity() 返回,可能大于 length()。

    使用向量数组的例子:

    #include <iostream>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        string s = "01234";
        cout << "s[" << s.length() << "]=\"" << s << "\"" << endl; 
        cout << "sizeof(s)=" << sizeof(s) << " (implementation dependent)" << endl;
        cout << endl;
    
        s += "56789012345";
        cout << "s[" << s.length() << "]=\"" << s << "\"" << endl; 
        cout << "sizeof(s)=" << sizeof(s) << " (implementation dependent)" << endl;
        cout << endl;
    
        vector<string>vs={"12","23","345","456","567","67888","7899999999","8","9876543210"};
    
        cout << "vs[" << vs.size() << "]={";
        size_t sz=0;
        for (size_t index=0; index<vs.size(); index++)
        {
            sz+=vs[index].size();
            if (index>0)
                cout << ",";
            cout << "\"" << vs[index] << "\":" << vs[index].size();
        }
        cout << "}:" << sz << endl;
        cout << "sizeof(vs)=" << sizeof(vs) << " (implementation dependent)" << endl;
    
        return 0;
    }
    

    结果:

    s[5]="01234"
    sizeof(s)=8 (implementation dependent)
    
    s[16]="0123456789012345"
    sizeof(s)=8 (implementation dependent)
    
    vs[9]={"12":2,"23":2,"345":3,"456":3,"567":3,"67888":5,"7899999999":10,"8":1,"9876543210":10}:39
    sizeof(vs)=24 (implementation dependent)
    

    【讨论】:

      【解决方案6】:
      template< class T, size_t N >
      std::size_t Length(const T(&)[N])
      {
          return N;
      };
      
      std::cout << Length(another_array) << std::endl;
      

      【讨论】:

        【解决方案7】:

        在String向量中使用size()方法

        【讨论】:

          【解决方案8】:

          需要注意的一点:文本可以用不同的方法表示。文本数组也可以用不同的方法表示。

          指向 C 风格字符串的指针数组

          一种常见的方法是拥有一个指向char 的指针数组。问题是数组的大小并不代表所有文本的大小。此外,还必须建立数据或指针的所有权,因为可能必须删除文本(被调用者可以删除文本还是调用者删除文本?)。因为它是一个数组,所以数组的大小必须始终伴随数组的所有参数(除非数组总是固定大小)。

          char 数组 - 压缩文本

          另一种方法是传递char 的数组,并使字符串在数组中连续。一个字符串跟随前一个的终止字符。使用这个数组,可以表示所有字符串的总大小,不会浪费空间。同样,对于数组,数组的大小在传递时必须伴随数组。

          std::string 的数组

          在 C++ 中,可以使用std::string 表示文本。在这种情况下,数组表示字符串的数量(类似于上面的 C-Strings 数组)。要获得所有字符串的总大小,必须将每个单独字符串的大小相加。由于这是一个数组,因此还必须传递数组的大小。

          总结

          在运行时,数组大小必须在数组被传递时伴随数组。 sizeof 仅在编译时处理。一个更简单的结构是std::vector,它动态地处理大小和内存分配。

          【讨论】:

            猜你喜欢
            • 2023-03-07
            • 1970-01-01
            • 2020-10-18
            • 2014-04-24
            • 2014-11-01
            • 2010-10-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多