我使用以下宏来计算数组的大小:
#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
答案 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::begin
和std::end
。 Const版本std::cbegin
和std::cend
也将进入标准的未来版本(C ++ 14?),并且可能已经在您的编译器中。如果您可以访问标准功能,甚至不要考虑使用下面的功能。
不是仅将数组的起始地址作为指针传递,或者将指针加上其他人建议的大小,而是从标准库中获取提示并将两个指针传递给数组的开头和结尾。这不仅使您的代码更像现代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风格数组的行为)。