计算数组的大小

时间:2009-04-06 02:16:01

标签: c++ c

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

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

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

DP

#include <stdio.h>

#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

9 个答案:

答案 0 :(得分:27)

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

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

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

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

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

答案 1 :(得分:12)

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

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

答案 2 :(得分:10)

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

template<typename T, size_t N> 
size_t G_N_ELEMENTS( T (&array)[N] )
{
  return N;
}

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

// ArraySize
template<typename T> 
struct ArraySize;

template<typename T, size_t N> 
struct ArraySize<T[N]> 
{ 
  enum{ value = N };
};

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

答案 3 :(得分:4)

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

#include <stdio.h>

#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<int>之类的东西。

答案 4 :(得分:4)

编辑:自编写此答案以来,我们引入了C ++ 11,它包含的功能与我在下面显示的完全相同:std::beginstd::end。 Const版本std::cbeginstd::cend也将进入标准的未来版本(C ++ 14?),并且可能已经在您的编译器中。如果您可以访问标准功能,甚至不要考虑使用下面的功能。

<小时/> 我想在Benoît's answer上建立一点。

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

template<typename T, int N>
T * BEGIN(T (& array)[N])
{
    return &array[0];
}

template<typename T, int N>
T * END(T (& array)[N])
{
    return &array[N];
}

template<typename T, int N>
const T * BEGIN_CONST(const T (& array)[N])
{
    return &array[0];
}

template<typename T, int N>
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。

答案 5 :(得分:2)

如果你稍微改变foo功能,可能会让你感觉更舒服一点:

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

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

答案 6 :(得分:2)

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 ++常见问题解答:http://www.parashift.com/c++-faq-lite/containers.html

记住:“阵列是邪恶的”

答案 7 :(得分:1)

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

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

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

答案 8 :(得分:0)

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

template<typename T, std::size_t size>
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<int, 100> const & array);

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