计算数组的长度

5

我有一个C++数组,声明如下:

CString carray[] =
{
        "A",
        "B",
        "C",
        "D",
        "E"
}

我希望在运行时确定的长度。 我正在进行以下操作:
int iLength = sizeof(carray)/sizeof(CString);

这个正确吗?


是的,我也是这样做的,就像你一样,我正在等待看是否有其他方法。 - Narendra N
将此问题重命名为“C语言中数组的长度”可能是一个好主意。 - Brock Woolf
4
这并不是在运行时“确定”长度,长度是编译时常量,给定的表达式也是常量,所以编译器可以在编译期间简单地计算它并替换为正确的值。此外,考虑去掉括号;sizeof不是一个函数。 - unwind
@Brock:我做了...我不明白你的评论。Timbo的代码和问题中的一个版本一样是编译时常量。当然,它更好,因为它避免了重复类型,并且没有什么主要的错误,这就是它的实现方式。我只是指出它不是运行时计算。 - unwind
Brook Woolf,您可能是指“在C++中C风格数组的长度”。 - avakar
10个回答

24
你可以使用以下函数模板。如果你在使用Boost,可以调用boost::size函数。点击此处了解更多信息。
template <typename T, std::size_t N>
std::size_t size(T (&)[N])
{
    return N;
}

int iLength = size(carray);

正如其他人已经提到的那样,但是你应该更喜欢使用std::vector而不是C风格的数组。


3
+1 - 要明确的是,这种方法应该在C++中使用(显然在C语言中不起作用),因为如果您意外地尝试使用指针,它将生成编译时错误,而sizeof(ptr)/ sizeof(ptr [0])方法会愉快地并且悄无声息地给出错误的结果。 - Michael Burr

17

是的。如果声明的元素类型发生了变化,你也可以写成:

int iLength = sizeof(carray)/sizeof(carray[0]);

这对于C语言很有效,但是对于C++语言,请参考avakar的答案以获得更好的替代方案。此外,考虑使用非习惯性的数组索引顺序,以防止从重载operator[]的类型中获取潜在不正确的信息:sizeof(array)/sizeof([0]array)。 - Michael Burr
1
你可能想表达的是 sizeof(array)/sizeof(0[array])。 :) 这是一个有趣的想法。 - avakar
1
为了少点戏剧性,可以使用 sizeof(array)/sizeof(*array)。这也很容易记住! - Saradhi

6

这是正确的,因为它使用元编程,如下所示:

template <typename T, std::size_t N>
inline std::size_t array_size( T (&)[N] ) {
   return N;
};

你必须知道,这只有在编译器看到数组定义时才有效,在传递给函数后(其中它会衰减为指针),就不再有效:

void f( int array[] )
{
   //std::cout << array_size( array ) << std::endl; // fails, at this point array is a pointer
   std::cout << sizeof(array)/sizeof(array[0]) << std::endl; // fails: sizeof(int*)/sizeof(int)
}
int main()
{
   int array[] = { 1, 2, 3, 4, 5 };
   f( array );
   std::cout << array_size( array ) << std::endl; // 5
   std::cout << sizeof(array)/sizeof(array[0]) << std::endl; // 5 
}

1
@avakar:与大多数地方一样,inline可能是不需要或被忽略的,最终只是给编译器的一个建议。但这并不是多余的,编译器可以决定在自己的权利下为每个调用生成函数(无论是否使用inline),即使大多数当前的编译器会内联这样一个小函数。 - David Rodríguez - dribeas
这是多余的,因为函数模板默认是inline的。此外,请注意,inline关键字现在与代码内联几乎没有任何关系。它只是标记函数,以便链接器接受多个(但相同的)定义。 - avakar
1
@avakar: 我已经回归标准,没有找到任何地方说函数模板默认是内联的。与模板和内联相关的唯一交叉引用是函数模板(以及函数模板方法)可以内联或不内联,但没有关于它们默认为内联的信息。 - David Rodríguez - dribeas
我的错,你是对的,我已经检查了标准,它们确实不是默认的内联。但是它们不必遵守一个定义规则,因此内联修饰符是无意义的。 - avakar
使用模板可以在多个编译单元中定义符号,这些单元会被链接到库或可执行文件中,而不会产生重复定义的链接错误,但是模板必须遵守一次定义规则。一个常见错误是在一个编译单元中使用模板,然后在另一个编译单元中使用相同模板的特化版本。当发生这种情况时,你最好得到的结果是分段错误(至少你会注意到有什么问题),最坏的结果则是出现意外行为。 - David Rodríguez - dribeas
我改正了。我指的是第3.2/3段,第3.2节名为One Definition Rule,这让我感到困惑。尽管如此,我仍然坚持我的第一条评论。就标准而言,你的程序在所有方面都等同于一个删除了inline关键字的程序。现代编译器也是如此,它们通常不再将inline视为优化提示(或者至少允许从命令行设置此行为)。 - avakar

5

这段代码是正确的,但在大多数情况下,在C++中处理数组有更好的方法。特别是因为这种方法无法处理动态大小的数组。

对于这种情况,请使用标准库类std::vector代表动态大小的数组(即可以插入和删除条目)。


2
而boost::array适用于静态大小的数组。 - GManNickG

1

是的,这是正确的方法,但它只适用于在编译时已知数组大小并且在sizeof( array )语句处可见的情况下。对于动态大小的数组,它将无法工作 - 您需要使用其他方法,例如使用容器如stl::vector或传递/存储元素数量作为单独的参数。


0
最好的方法是使用宏,在需要大小的任何地方使用它。
#define MAX_ROWS 1048
int array[MAX_ROWS];

这样,即使在将数组作为参数传递的函数中,您也可以使用MAX_ROWS。

0

这不是运行时,这是编译时。你使用的方式是正确的。请注意,Visual Studio定义了一个名为_countof的函数,它执行相同的操作。

在运行时无法确定长度。您可以自己保存长度,或使用std::vector


现有的代码已经保留了数组的长度。但是当数组发生变化(任何元素从数组中移除)时,我必须修改数组的长度。我不能使用std:vector,因为我必须在许多地方进行修改。 - Ashish Ashu
1
当你从向量中删除一个元素时,它不会自动调整大小。此外,在分配向量时,它可能不是您指定的大小。 - graham.reeds

0
如果您有一个动态数组,您应该使用类似于STL向量的东西。http://www.cppreference.com/wiki/stl/vector/start C++中的普通数组是固定大小的,您需要手动分配内存来扩展动态数组。
如果您在编译时知道数组大小,请使用常量而不是计算。

我正在使用常量,但数组可能在运行时更改(任何元素都可以被删除或添加)。如果数组发生更改,则必须重新修改常量值。我不能使用std:vector,因为这个数组在许多地方都被使用..请建议一些解决方法。 - Ashish Ashu
1
为什么不能使用vector呢?必须要在很多地方使用它并不是一个理由。事实上,对于C风格的数组也是一样的。 - Konrad Rudolph
不确定为什么被降级,但这是关于数组的一些基础知识:http://www.cplusplus.com/doc/tutorial/arrays/ - Glenn
3
你的答案没有任何问题。sizeof是一个编译时的结构,所以最好记住大小而不是计算它! - Abhay

0
请阅读 Dr. Dobbs 杂志上 Ivan J. Johnson 的 this 文章。我认为它涵盖了这里所介绍的解决方案的大部分内容,并非常好地概述了每种方法的优点和缺点。

0

Windows SDK(即windows.h头文件)提供了宏ARRAYSIZE,以安全的方式实现此功能(即不使用非静态数组)


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