如何获取数组的长度?

687

有没有办法找出数组有多少个值?检测我是否已经到达数组的末尾也可以。


30
数组是从哪里来的?通常,处理数组的函数也会带有一个长度参数以解决这个问题。 - Michael Myers
3
我正在制作一个“Mad Libs(疯狂填词)”程序,其中包含一个文本数组,以及用户需要填写的名词/动词的位置。我想使用一个函数遍历整个数组,并用用户输入的文本替换“[noun]”和“[verb]”的值。 - Maxpm
可能是计算数组长度的重复问题。 - Maxpm
9
请注意,在C语言中,数组不是对象或结构体。因此,默认情况下没有存储任何长度参数。如果您想将它们作为对象在C++中使用,请使用C++对象std::vector,或者如果可能的话,C++11的std::array。如果必须使用指针,请始终将数组的长度作为第二个参数传递给每个与其一起工作的函数。 - Pihhan
1
如果您正在使用C++20,那么我也已经添加了答案。由于这里有很多答案,它可能会被轻易忽略。 - gprathour
std::size() - undefined
31个回答

643

如果您指的是C风格数组,那么可以这样做:

int a[7];
std::cout << "Length of array = " << (sizeof(a)/sizeof(*a)) << std::endl;

这个方法对指针无效(即以下两种情况都不起作用):

int *p = new int[7];
std::cout << "Length of array = " << (sizeof(p)/sizeof(*p)) << std::endl;

或:

void func(int *p)
{
    std::cout << "Length of array = " << (sizeof(p)/sizeof(*p)) << std::endl;
}

int a[7];
func(a);

如果你想在C++中实现这种行为,那么你应该使用一个容器类,可能是std::vector


119
如果您将数组传递到另一个函数并在那里尝试这样做,它也不起作用 :) - San Jacinto
31
@San Jacinto: 不管您在哪个函数中,这都适用于数组。然而,将可变长度的数组作为参数传递给函数是不可能的(它会衰变为指针)- 但如果您将一个数组放在结构体内部,那么这将按预期工作。 - eq-
1
@OliverCharlesworth 如果你将数组按值传递到另一个函数中并在那里尝试,它不会起作用,对吗?问题是为什么。 - A_Matar
6
在C或C++中,你不能通过值传递一个数组。 - Oliver Charlesworth
@Dineshkumar:它在C++中可以工作。但是在C++中,你不应该需要这样做。 - Oliver Charlesworth
显示剩余8条评论

187

如其他人所说,你可以使用 sizeof(arr)/sizeof(*arr),但是对于不是数组的指针类型,这将会给出错误的答案。

template<class T, size_t N>
constexpr size_t size(T (&)[N]) { return N; }

这个方法有一个不错的特点,它对于非数组类型无法编译(Visual Studio有一个名为_countof的函数也有这个特性)。constexpr使它成为编译时表达式,因此与宏相比没有任何缺陷(至少我不知道有哪些)。

你还可以考虑使用C++11的std::array,它在本地C数组上没有额外开销并公开了其长度。

C++17<iterator>头文件中提供了std::size()函数,可以用于STL容器和具有相同功能(感谢@Jon C)。


编译时我收到了两个错误(我甚至没有尝试使用它): error C2265: '<Unknown>' : 引用零大小的数组是非法的 error C2266: '<Unknown>' : 引用非常数有界数组是非法的 问题出在哪里? - jimifiki
1
谢谢Motti!遗漏的参数名称对我来说已经很清楚了。但令人难以置信的是,我似乎以前从未使用过_refs/pointers to arrays_。而且可能在将来也不会使用,因为这样的数组正在逐渐消失。 - yau
1
如果使用C++11,不是std::extent更好的解决方案吗????https://en.cppreference.com/w/cpp/types/extent - Isaac Pascual
2
@IsaacPascual 我之前不熟悉 extent,现在看了一下,发现它有两个特点使得它对于这种用例来说不如上面的函数实用。第一,它对于指针返回零(而不是编译错误)。第二,它需要一个类型参数,因此为了检查一个变量,你必须使用 decltype - Motti
显示剩余5条评论

123

虽然这是一个老问题,但值得更新到C++17的答案。在标准库中,现在有了模板函数std::size(),它返回std容器或C风格数组中元素的数量。例如:

#include <iterator>

uint32_t data[] = {10, 20, 30, 40};
auto dataSize = std::size(data);
// dataSize == 4

2
使用C++20,可以使用std::ssize()函数获取任何范围的std::size()作为有符号整数,这对于在循环中避免包装错误、冗长的强制转换等非常有用。 - underscore_d
你应该注意,这种方法对于传入函数的数组无效,你需要在定义数组的块中执行此操作,并将其作为额外参数传递给函数。 - Mark Ransom

113

使用 sizeof myArray 可以获取为该数组分配的总字节数。然后,通过将其除以数组中一个元素的大小,可以找出数组中元素的数量:sizeof myArray[0]

因此,您会得到类似以下的东西:

size_t LengthOfArray = sizeof myArray / sizeof myArray[0];

sizeof会计算出一个size_t类型的值,所以LengthOfArray的结果也将是这种类型。


4
这是一个非常简单易行的解决方案,可克服看似存在已久的问题。 - user2993456
4
不适用于由指针持有的C++“new”数组。如果您对它进行解引用,则可以获得指针的大小(4个字节)或其第一个元素的大小。 - DragonLord
1
@DragonLord 是的,尽管使用关键字new声明数组大小的任何人都已经知道运行时数组的大小,因此在这种情况下没有必要使用sizeof运算符来查找数组的大小。我相信你知道这一点。这只是为了让不知道的人受益。 - Martyn Shutt
@surega 它不会崩溃。 - CITBL

67

有没有办法找出数组有多少个值?

有的!

尝试使用 sizeof(array)/sizeof(array[0])

检测我是否已经到达了数组的末尾也可以。

除非你的数组是字符数组(即字符串),否则我看不到任何方法可以实现这一点。

P.S:在C++中,始终使用std::vector。它有几个内置函数和扩展功能。


这对于变量个体数组大小不起作用。 - Don Larynx
4
对于向量(vector)加1。现在很少有使用旧式C++数组的强有力理由了。除非数组的大小永远不会改变,但即使如此,也应该使用数组容器类。最好使用像vector这样的容器类来进行动态数组存储。使用容器类的优点远远超过了必须管理自己的内存的缺点。 - Martyn Shutt
@MartynShutt 当你在缓存绑定的情况下,比如在游戏开发中,有时候你不能承担使用向量的代价。 - Tara
如果一个数组没有元素,那么array[0]是否合法? - mercury0114
@mercury0114 在C++中,长度为0的数组是非法的。即使它们不是非法的,你仍然可以对array[0]使用sizeof运算符进行求值,因为它实际上并不在运行时计算,只是在编译时找到它的类型以获取大小。 - CoffeeTableEspresso

35
#include <iostream>

int main ()
{
    using namespace std;
    int arr[] = {2, 7, 1, 111};
    auto array_length = end(arr) - begin(arr);
    cout << "Length of array: " << array_length << endl;
}

11
我相信这个只适用于存储在栈上的局部变量。 - DragonLord
1
这是最好的答案。@DragonLord,它同样适用于成员变量。请参见http://cpp.sh/92xvv。 - Shital Shah

33

std::vector有一个方法size(),它返回向量中元素的数量。

(是的,这是一个开玩笑的答案)


11
可能带有些许玩笑,但这几乎肯定是正确的方法。 - Jerry Coffin
你为什么说这是半开玩笑的?对于我这个 C++ 新手来说,它似乎就是正确的答案。 - abcd
7
@dbliss 这是一种打趣的方式,因为原帖是在询问数组的长度,而eq-告诉他们如何获取C++向量的长度,这是两个不同的概念。然而,从更深层次上来看,它是正确的,因为如果需要在运行时获取长度,向量是更好的选择。请注意保持原文意思,并让翻译更通俗易懂,不要添加解释或其他内容。 - poolie
您也可以使用std::list或其他容器。 - BuvinJ
2
这个答案特别好的地方在于,你可以使用数组字面量初始化向量或列表。 - BuvinJ

30

这是一道古老而传奇的问题,已经有很多令人惊叹的答案了。但随着时间的推移,语言中不断添加新的功能,所以我们需要根据可用的新功能不断更新。

我刚刚注意到还没有人提到C++20。所以我想写下答案。

C++20

在C++20中,标准库中新增了一种更好的方法来查找数组的长度,即std:ssize()。该函数返回一个有符号值

#include <iostream>

int main() {
    int arr[] = {1, 2, 3};
    std::cout << std::ssize(arr);
    return 0;
}

C++17

在C++17中,对于相同的操作有了更好(那个时候)的方式,即std::size(),该函数被定义在iterator中。

#include <iostream>
#include <iterator> // required for std::size

int main(){
    int arr[] = {1, 2, 3};
    std::cout << "Size is " << std::size(arr);
    return 0;
}

另外,这种方法同样适用于vector

旧版

这种传统方法已经在许多其他答案中提到过了。

#include <iostream>

int main() {
    int array[] = { 1, 2, 3 };
    std::cout << sizeof(array) / sizeof(array[0]);
    return 0;
}

只是想告知一下,如果你想知道为什么这种方法在将数组传递给另一个函数时会失效。原因是:

C++ 中并不是按值传递数组,而是传递指向数组的指针。因为在某些情况下,传递整个数组可能是昂贵的操作。您可以通过将数组传递给某个函数,并在该函数中对数组进行一些更改,然后再次在主函数中打印数组来测试此操作,您将获得更新后的结果。

正如你已经知道的那样,sizeof() 函数返回字节数,因此在其他函数中,它将返回分配给指针的字节数,而不是整个数组的字节数。所以这种方法行不通。

但我相信你可以根据自己的需求找到好的解决方法。

祝编码愉快!


我想找到一个函数中元素的数量,你能建议我该怎么做吗? - Wakeel Furqan Ahmed

29
自C++11以来,引入了一些新的模板来帮助减轻处理数组长度时的痛苦。它们都定义在头文件<type_traits>中。
  • std::rank<T>::value

    如果T是数组类型,则提供成员常量值等于数组维度数。对于任何其他类型,值为0。

  • std::extent<T, N>::value

    如果T是数组类型,则提供成员常量值等于数组沿第N个维度的元素数量,如果N在[0,std::rank<T>::value)之间。对于任何其他类型,或者如果T是沿其第一个维度具有未知边界的数组,并且N为0,则值为0。

  • std::remove_extent<T>::type

    如果T是某种类型X的数组,则提供成员typedef type等于X,否则类型为T。请注意,如果T是多维数组,则仅删除第一个维度。

  • std::remove_all_extents<T>::type

    如果T是某种类型X的多维数组,则提供成员typedef type等于X,否则类型为T

要获取多维数组中任何维度的长度,可以使用decltypestd::extent结合使用。例如:

#include <iostream>
#include <type_traits> // std::remove_extent std::remove_all_extents std::rank std::extent

template<class T, size_t N>
constexpr size_t length(T(&)[N]) { return N; }

template<class T, size_t N>
constexpr size_t length2(T(&arr)[N]) { return sizeof(arr) / sizeof(*arr); }

int main()
{
    int a[5][4][3]{{{1,2,3}, {4,5,6}}, { }, {{7,8,9}}};

    // New way
    constexpr auto l1 = std::extent<decltype(a)>::value;     // 5
    constexpr auto l2 = std::extent<decltype(a), 1>::value;  // 4
    constexpr auto l3 = std::extent<decltype(a), 2>::value;  // 3
    constexpr auto l4 = std::extent<decltype(a), 3>::value;  // 0

    // Mixed way
    constexpr auto la = length(a);
    //constexpr auto lpa = length(*a);  // compile error
    //auto lpa = length(*a);  // get at runtime
    std::remove_extent<decltype(a)>::type pa;  // get at compile time
    //std::remove_reference<decltype(*a)>::type pa;  // same as above
    constexpr auto lpa = length(pa);
    std::cout << la << ' ' << lpa << '\n';

    // Old way
    constexpr auto la2 = sizeof(a) / sizeof(*a);
    constexpr auto lpa2 = sizeof(*a) / sizeof(**a);
    std::cout << la2 << ' ' << lpa2 << '\n';

    return 0;
}

顺便提一下,要获取多维数组中元素的总数:

constexpr auto l = sizeof(a) / sizeof(std::remove_all_extents<decltype(a)>::type);

或者将其放入函数模板中:
#include <iostream>
#include <type_traits>template<class T>
constexpr size_t len(T &a)
{
    return sizeof(a) / sizeof(typename std::remove_all_extents<T>::type);
}

int main()
{
    int a[5][4][3]{{{1,2,3}, {4,5,6}}, { }, {{7,8,9}}};
    constexpr auto ttt = len(a);
    int i;
    std::cout << ttt << ' ' << len(i) << '\n';
    
    return 0;
}

您可以通过跟随链接找到更多如何使用它们的示例。


19

还有 TR1/C++11/C++17 的方法(参见 Coliru 上的实例):

const std::string s[3] = { "1"s, "2"s, "3"s };
constexpr auto n       = std::extent<   decltype(s) >::value; // From <type_traits>
constexpr auto n2      = std::extent_v< decltype(s) >;        // C++17 shorthand

const auto     a    = std::array{ "1"s, "2"s, "3"s };   // C++17 class template arg deduction -- http://en.cppreference.com/w/cpp/language/class_template_argument_deduction
constexpr auto size = std::tuple_size_v< decltype(a) >;

std::cout << n << " " << n2 << " " << size << "\n"; // Prints 3 3 3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接