计算数组的大小

我使用以下宏来计算数组的大小:

#define G_N_ELEMENTS(arr) ((sizeof(arr))/(sizeof(arr[0]))) 

但是,当我计算函数中数组的大小(计算的值不正确)而不是调用函数的位置(计算的正确值)时,我看到它计算的值存在差异。 代码+输出如下。 任何想法,建议,提示等。 欢迎。

DP

 #include  #define G_N_ELEMENTS(arr) ((sizeof(arr))/(sizeof(arr[0]))) void foo(int * arr) // Also tried foo(int arr[]), foo(int * & arr) // - neither of which worked { printf("arr : %x\n", arr); printf ("sizeof arr: %d\n", G_N_ELEMENTS(arr)); } int main() { int arr[] = {1, 2, 3, 4}; printf("arr : %x\n", arr); printf ("sizeof arr: %d\n", G_N_ELEMENTS(arr)); foo(arr); } 

输出:

 arr : bffffa40 sizeof arr: 4 arr : bffffa40 sizeof arr: 1 

那是因为int *的大小是int 指针的大小(我使用的现代平台上有4或8个字节,但它完全取决于平台)。 sizeof是在编译时计算的,而不是运行时,因此即使sizeof (arr[])也无济于事,因为您可以在运行时使用许多不同大小的数组调用foo()函数。

int数组的大小是int 数组的大小。

这是C / C ++中棘手的一点 – 数组和指针的使用并不总是相同的。 在很多情况下,数组会衰减到指向该数组的第一个元素的指针。

至少有两种解决方案,兼容C和C ++:

  • 使用数组传入长度(如果函数的意图实际计算出数组大小则没有那么有用)。
  • 传递标记数据末尾的标记值,例如{1,2,3,4,-1}

这不起作用,因为sizeof是在编译时计算的。 该函数没有关于其参数大小的信息(它只知道它指向一个内存地址)。

考虑使用STL向量,或将数组大小作为参数传递给函数。

在C ++中,您可以像这样定义G_N_ELEMENTS:

 template size_t G_N_ELEMENTS( T (&array)[N] ) { return N; } 

如果您希望在编译时使用数组大小​​,请按以下步骤操作:

 // ArraySize template struct ArraySize; template struct ArraySize { enum{ value = N }; }; 

感谢j_random_hacker纠正我的错误并提供其他信息。

请注意,即使您尝试告诉C编译器函数中数组的大小,也不会提示(我的DIM等同于您的G_N_ELEMENTS ):

 #include  #define DIM(x) (sizeof(x)/sizeof(*(x))) static void function(int array1[], int array2[4]) { printf("array1: size = %u\n", (unsigned)DIM(array1)); printf("array2: size = %u\n", (unsigned)DIM(array2)); } int main(void) { int a1[40]; int a2[4]; function(a1, a2); return(0); } 

这打印:

 array1: size = 1 array2: size = 1 

如果您想知道函数内部的数组有多大,请将大小传递给函数。 或者,在C ++中,使用STL vector类的东西。

编辑:自从这个答案写完以来就引入了C ++ 11,它包含的函数完全按照我在下面显示的内容执行: std::beginstd::end 。 const版本std::cbeginstd::cbegin也将进入标准的未来版本(C ++ 14?),并且可能已经在您的编译器中。 如果您可以访问标准function,甚至不要考虑使用下面的function。


我想在Benoît的答案上做一点建设。

不是仅将数组的起始地址作为指针传递,或者将指针加上其他人建议的大小,而是从标准库中获取一个提示并将两个指针传递给数组的开头和结尾。 这不仅使您的代码更像现代C ++,而且可以使用arrays上的任何标准库算法!

 template T * BEGIN(T (& array)[N]) { return &array[0]; } template T * END(T (& array)[N]) { return &array[N]; } template const T * BEGIN_CONST(const T (& array)[N]) { return &array[0]; } template const T * END_CONST(const T (& array)[N]) { return &array[N]; } void foo(int * begin, int * end) { printf("arr : %x\n", begin); printf ("sizeof arr: %d\n", end - begin); } int main() { int arr[] = {1, 2, 3, 4}; printf("arr : %x\n", arr); printf ("sizeof arr: %d\n", END(arr) - BEGIN(arr)); foo(BEGIN(arr), END(arr)); } 

如果模板不起作用,这是BEGIN和END的替代定义。

 #define BEGIN(array) array #define END(array) (array + sizeof(array)/sizeof(array[0])) 

更新:上面的模板代码在MS VC ++ 2005和GCC 3.4.6中工作,应该如此。 我需要一个新的编译器。

我也在重新思考这里使用的命名约定 – 伪装成宏的模板函数感觉不对。 我相信我很快就会在自己的代码中使用它,我想我将使用ArrayBegin,ArrayEnd,ArrayConstBegin和ArrayConstEnd。

如果你稍微改变foofunction,它可能会让你感觉更舒服:

 void foo(int * pointertofoo) { printf("pointertofoo : %x\n", pointertofoo); printf ("sizeof pointertofoo: %d\n", G_N_ELEMENTS(pointertofoo)); } 

这就是编译器将看到的东西与函数完全不同的上下文。

 foo(int * arr) //Also tried foo(int arr[]), foo(int * & arr) { // - neither of which worked printf("arr : %x\n", arr); printf ("sizeof arr: %d\n", G_N_ELEMENTS(arr)); } 

sizeof(arr)是sizeof(int *),即。 4

除非你有充分的理由编写这样的代码,否则不要。 我们现在处于21世纪,使用std :: vector代替。

有关详细信息,请参阅C ++ FAQ: http : //www.parashift.com/c++-faq-lite/containers.html

记住:“arrays是邪恶的”

你应该只在数组上调用sizeof。 当您在指针类型上调用sizeof时,大小将始终为4(或8,或您的系统所做的任何事情)。

MSFT的匈牙利表示法可能很难看,但是如果你使用它,你就知道不要在任何以’p’开头的东西上调用你的宏。

还要检查WinNT.h中ARRAYSIZE()宏的定义。 如果你正在使用C ++,你可以使用模板做一些奇怪的事情来获得编译时断言,如果这样做的话。

既然我们在C ++ 11中有constexpr ,那么类型安全(非宏)版本也可以用在常量表达式中。

 template constexpr std::size_t array_size(T const (&)[size]) { return size; } 

与宏解决方案不同,它将无法在无法正常工作的情况下进行编译(它不会偶然对指针起作用)。 您可以在需要编译时常量的地方使用它:

 int new_array[array_size(some_other_array)]; 

话虽这么说,如果可能的话,最好还是使用std::array 。 不要注意那些说使用std::vector的人,因为它更好。 std::vector是一种具有不同强度的不同数据结构。 与C风格的数组相比, std::array没有开销,但与C风格的数组不同,它在最轻微的挑衅时不会衰减到指针。 另一方面, std::vector要求所有访问都是间接访问(通过指针)并使用它需要动态分配。 如果你习惯使用C风格的数组,要记住的一件事是确保将std::array传递给这样的函数:

 void f(std::array const & array); 

如果未通过引用传递,则会复制数据。 这遵循大多数设计良好的类型的行为,但在传递给函数时不同于C风格的数组(它更像是结构内部的C风格数组的行为)。