C++中数组的元素个数

31
假设我有一个数组arr。什么情况下以下方法不能给出数组元素的数量:sizeof(arr) / sizeof(arr[0])
我只能想到一种情况:数组包含与该数组类型不同的派生类型。
我是否正确?除此之外还有其他情况(我几乎可以确定还有)吗?
抱歉这个问题很琐碎,我是Java开发人员,对C++相当新手。
谢谢!

7
请注意,数组中的所有对象始终具有相同的类型。如果您将子类对象放入其中,则会发生切片现象。 - Björn Pollex
使用STL容器,例如std::vector。 - riviera
GManNickG和Nawaz的答案都是正确的。 @Nawaz提到了将数组传递给函数,然后在被调用的函数内计算计数的情况。 - proton
14个回答

39

假设我有一个数组arr。什么情况下以下代码不会返回数组元素的数量:sizeof(arr) / sizeof(arr[0])?

我经常看到新手程序员犯这样的错误:

void f(Sample *arr)
{
   int count = sizeof(arr)/sizeof(arr[0]); //what would be count? 10?
}

Sample arr[10];
f(arr);

新手程序员们认为count的值将会是10。但这是错误的。

即使是这样也是错误的:

void g(Sample arr[]) //even more deceptive form!
{
   int count = sizeof(arr)/sizeof(arr[0]); //count would not be 10  
}

这是因为一旦你将数组传递给任何一个函数,它会变成指针类型,所以sizeof(arr)会给出指针的大小,而不是数组的大小!


编辑:

以下是一种优雅的方式,您可以将数组传递给函数,而不让它退化为指针类型:

template<size_t N>
void h(Sample (&arr)[N])
{
    size_t count = N; //N is 10, so would be count!
    //you can even do this now:
    //size_t count = sizeof(arr)/sizeof(arr[0]);  it'll return 10!
}
Sample arr[10];
h(arr); //pass : same as before!

2
Nawaz 在这里是正确的,新手程序员有时会犯这个错误,因为他们不理解在 f() 中 arr 可以被用作数组,但函数 f() 只知道 arr 是一个指针(因此 sizeof(arr) == sizeof(Sample *))。这是对一个有效且有用的结构的不当使用,只有在正确使用时才能发挥其作用。 - par
谢谢!所以在这种情况下,需要提供数组的元素数量? - Albus Dumbledore
1
@阿不思·邓布利多:请看我回答中的编辑。它为您解释了一个好的解决方案! - Nawaz
2
@Albus:通常,如果您的函数适用于范围,则应设计为使用迭代器(或您最喜欢的通用范围方法),而不是特定的容器。 - GManNickG
@GMan,我对STL的了解不是很深入,但你说的听起来即使对于一个Java开发者来说也是合理的。 - Albus Dumbledore
显示剩余7条评论

10
自C++17起,您还可以使用标准化的自由函数:std::size(container),它将返回该容器中元素的数量。
示例:
std::vector<int> vec = { 1, 2, 3, 4, 8 };
std::cout << std::size(vec) << "\n\n";    // 5

int A[] = {40,10,20};
std::cout << std::size(A) << '\n';      // 3

这非常棒。 - Matthew McCord

9

C++中的数组与Java中的数组非常不同,因为它们是完全未经管理的。编译器或运行时根本不知道数组的大小。

如果在声明中定义了大小,则只有在编译时才能知道信息:

char array[256];

在这种情况下,sizeof(array)将为您提供正确的大小。
然而,如果您将指针用作数组,“array”将只是一个指针,并且sizeof不会提供关于数组实际大小的任何信息。
STL提供了许多模板,允许您拥有数组,其中一些具有大小信息,一些具有可变大小,大多数具有良好的访问器和边界检查。

+1。这不仅适用于数组。在C++中,许多东西都没有像Java那样进行管理。您需要记住要做很多清理工作(释放先前分配的变量和内存)。 :) - GolezTrol
6
你不需要记住任何东西,可以使用智能指针。 - GManNickG

9
给定一个数组arr,根据数组和sizeof的定义,sizeof(arr) / sizeof(arr[0])的值始终等于元素数量,没有任何例外情况。实际上这个结论已经在§5.3.3/2中明确指出:

当应用于数组时,结果是数组中的总字节数。 这意味着n元素的数组大小是每个元素大小的n倍。

重点在于除以一个元素的大小sizeof(arr[0]),得到n的值。

这个表达式是在编译时确定的常量吗?因为任何其他实现都是基于指针完成的。 - deceleratedcaviar
@Daniel:这是一个常量表达式,但恐怕我不理解你问题的后半部分。 - GManNickG
2
回到它时,我也不理解它。谢谢回复。 - deceleratedcaviar

4

不,这样仍然会产生正确的值,因为您必须将数组定义为单个类型的所有元素或该类型的指针。在任何情况下,数组大小在编译时已知,因此sizeof(arr)/ sizeof(arr [0])始终返回元素计数。

以下是如何正确使用它的示例:

int nonDynamicArray[ 4 ];

#define nonDynamicArrayElementCount ( sizeof(nonDynamicArray) / sizeof(nonDynamicArray[ 0 ]) )

在这里我想再进一步说明使用这种方法的何时是合适的。你不会经常使用它,主要情况是你想定义一个数组,以便稍后添加元素时不需要改变很多代码,这种构造方式主要用于维护。当我考虑到这个问题时,经典的例子就是为某个程序构建命令表,然后打算稍后添加更多命令。在此示例中,如果你要维护/改进你的程序,则只需将另一个命令添加到数组中,然后添加命令处理程序:

char        *commands[] = {  // <--- note intentional lack of explicit array size
    "open",
    "close",
    "abort",
    "crash"
};

#define kCommandsCount  ( sizeof(commands) / sizeof(commands[ 0 ]) )

void processCommand( char *command ) {
    int i;

    for ( i = 0; i < kCommandsCount; ++i ) {
        // if command == commands[ i ] do something (be sure to compare full string)
    }
}

这不是真的。或者,它只对固定大小的数组有效。 - GolezTrol
2
@GolezTrol:这是永远正确的。按照定义,数组的大小是固定的。 - GManNickG
对于在编译时定义的数组,这总是成立的。如果您将数组定义为指向类型的指针,然后使用动态分配(new),则此构造不起作用。 - par
@Fred:说得对,但我的意思是在使用 sizeof 获取数组大小的上下文中。 - GManNickG
可能是提问者在使用另一种类型,对吧...我的意思是,在问题的背景下,sizeof 没有起作用。 :) - GolezTrol
显示剩余2条评论

2

_countof(my_array)在MSVC中的用法

我只能想到一种情况:数组包含不同派生类型的元素。

C++中的数组元素是对象,而不是指针,因此您不能将派生类型对象作为元素。

就像上面提到的那样,sizeof(my_array)(以及_countof())仅适用于数组定义的范围内。


1
首先,您可以通过使用 std::vector 而不是数组来规避该问题。其次,如果您将派生类的对象放入超类的数组中,则会出现切片问题,但好消息是,您的公式仍然有效。在 C++ 中实现多态集合需要使用指针。这里有三个主要选项:

C++中的多态集合是通过指针实现的 - 哈哈! - Albus Dumbledore
1
@Albus:只是为了让这个问题变得完全清晰。除非你使用一些需要它的API,否则没有理由优先选择数组而不是vector。使用向量并使用shared_ptr - Björn Pollex
1
@Space:我不同意。如果我知道在编译时需要n个元素,而且n很小,我不明白为什么要浪费时间使用std::vector - GManNickG
1
@Albus:向量保证使用连续的内存块来存储元素,因此它与数组一样快。 - Björn Pollex
1
@Albus: 测试而不是假设。 向量确实需要至少一次分配,而(非动态)数组则不需要,但我强烈怀疑这对于10MP图像来说并不重要。 - Fred Nurk
显示剩余5条评论

1
假设我有一个数组arr。 什么情况下以下操作不会给出数组元素的数量:sizeof(arr) / sizeof(arr[0])
arr实际上不是数组(而是指向初始元素的指针)时,就会发生这种情况。其他答案解释了这是如何发生的。
我只能想到一种情况:数组包含与数组类型的派生类型不同的元素。
这是不可能的(基本上和Java数组与泛型不兼容的原因相同)。 数组是静态类型的; 它保留了为特定类型(基本类型)大小调整的内存“插槽”。
对于这个简单问题感到抱歉,我是一名Java开发人员,对C ++还很陌生。

C++数组不是一等对象。您可以使用boost::array使它们更像Java数组,但请记住,您仍将具有值语义而不是引用语义,就像其他所有内容一样。(特别是,这意味着您无法真正声明类似于Java中的Foo[]类型的变量,也无法用大小不同的另一个数组替换数组;数组大小是类型的一部分。)在此类中使用.size(),在Java中使用.length。(它还提供了提供C++迭代器的常规接口的迭代器。)


1
使用Microsoft的"_countof(array)"宏。这个链接到Microsoft Developer Network解释了它,并提供了一个示例,演示了"sizeof(array)"和"_countof(array)"宏之间的区别。

Microsoft和"_countof(array)"宏


1

看起来,如果您知道数组中元素的类型,您也可以利用sizeof来优化代码。

int numList[] = { 0, 1, 2, 3, 4 };

cout << sizeof(numList) / sizeof(int);

// => 5

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