C中的array_length

时间:2010-01-18 20:44:42

标签: c arrays

我写了一个像这样的array_length函数:

int array_length(int a[]){
    return sizeof(a)/sizeof(int);
}

然而当我做的时候它会返回2

unsigned int len = array_length(arr);
printf ("%i" , len);

我在哪里

int arr[] = {3,4,5,6,1,7,2};
int * parr = arr;

但是当我做的时候

int k = sizeof(arr)/sizeof(int);
printf("%i", k);

在main函数中,它返回7.

编写array_length函数的正确方法是什么?如何使用它?

14 个答案:

答案 0 :(得分:16)

计算数组长度(以C为单位)充其量只是个问题。

以上代码的问题在于:

int array_length(int a[]){ 
    return sizeof(a)/sizeof(int); 
} 

您只是将指针作为“a”传递,因此sizeof(a)sizeof(int*)。如果您使用的是64位系统,那么函数内部的sizeof(a)/sizeof(int)总是会得到2,因为指针将是64位。

您可以(可能)将此作为宏而不是函数执行,但这有它自己的问题...(它完全内联这一点,因此您获得与int k =...块相同的行为。 )

答案 1 :(得分:12)

您的功能无效。 C数组和C指针是不同的类型,但如果你看它有趣,数组将退化成指针。

在这种情况下,您将数组作为参数传递,并且它在调用中变为指针,因此您正在测量sizeof(int *)/sizeof(int)

使这项工作的唯一方法是使用宏:

#define ARRAYSIZE(x) (sizeof(x)/sizeof(*x))

只有在该范围内将x声明为数组而不是指针时才会有效。

答案 2 :(得分:8)

使用宏...

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

它还可以为任何数组数据类型工作:)

答案 3 :(得分:5)

通常,无法测量数组大小C.在主函数中,编译器会为您在大括号之间编写的元素进行计数,因此您实际上是在声明int arr[7]。这有你期望的尺寸。

但是,在您的函数中,int a[]等同于int *a - 指向整数的指针。 知道它是一个数组,所以有更多整数跟随,但你的array_length函数可以传递任何整数指针,所以它不能知道。

这是尽可能使用std::vector而不是原始数组的众多原因之一。

答案 4 :(得分:5)

您的问题的简单答案是:无法编写array_length函数。您可能能够使用宏定义,但这取决于您将使用宏的上下文。

你在C中混淆了数组和指针的常见错误。在C中,数组的名称,在大多数情况下,相当于指向其第一个元素的指针。您的array_length函数在这样的上下文中接收数组a。换句话说,不可能在C中将数组作为数组传递。您的函数就好像它是这样定义的:

int array_length(int *a){
    return sizeof(a)/sizeof (int);
}

,基本上将int *的大小除以int的大小。另外,通过上面的描述,在函数中知道C中数组的大小是不可能的

现在,如何在功能之外正确确定其大小?答案是sizeof运算符是数组名称​​不减少为指向其第一个元素的指针的情况之一。我在this answer中更精细地解释了这些差异。另请注意,尽管有外观,但sizeof是一个操作符,而不是一个函数(正如我们刚刚学到的那样,不能是一个函数,因为它将无法计算数组的大小。)

最后,要确定任何类型a的数组T的大小,我更喜欢:

size_t sz = sizeof a / sizeof a[0];

以上是与类型无关的:a可以是上述任何类型。实际上,您甚至可以更改a的类型,而无需更改上述内容。

答案 5 :(得分:3)

尝试_countof它在WinNT.h中定义为

// Return the number of elements in a statically sized array.
//   DWORD Buffer[100];
//   RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))

答案 6 :(得分:3)

问题是函数参数实际上不是数组,即使C允许你做一个看起来像一个的声明。该参数最终成为一个简单的指针。我说elsewhere

  

这归结为C / C ++中的普通数组参数是虚构的事实 - 它们确实是指针。应尽可能避免数组参数 - 它们实际上只是混淆了问题。

这就是为什么这种类型的构造返回数组中元素的数量最终成为C中的一个宏。为了我认为宏的一个好的(如果复杂的)实现,请参阅此前的SO答案:

为便于参考,这是宏:

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

该宏的复杂性使得它比大多数使用起来更安全(请注意,我并不声称自己是宏中使用的技术的创始人)。

答案 7 :(得分:2)

计算数组中元素数量的一般过程是sizeof arr / sizeof arr[0](或sizeof arr / sizeof *arr)。说完了......

编写一个函数来计算作为参数传递的数组的长度注定要失败,因为函数接收的是指针,而不是数组。当您使用数组表达式作为参数调用函数时,数组表达式将其类型隐式地从“数组T”转换为“指向T的指针”,并且其值将设置为指向数组中的第一个元素。您的函数没有看到数组对象;它看到了一个指针。

在函数参数声明的上下文中,int a[]int *a完全相同,但对于函数参数声明,为真(没有什么能比我更开心看到从C的所有未来版本中消除的第一个形式,但这不会发生)。

答案 8 :(得分:2)

int arr [whatever]函数参数列表中定义了一个指针,而不是一个数组。因此,有关长度的信息将永远丢失。

为什么!?

要了解原因,您必须了解C的全部内容。 C永远不会隐含地复制复杂的块。因此,当你说“传递给我一个数组”时,它实际上意味着“我想传递一个地址,通常是这个数组的名称”。

这不是一个缺点。如果您想要更多,则必须手动传递。奖励是你确切地知道机器的水平是什么,给你性能和其他好处。这就是为各种目的提供一种通用编程语言的方法。

答案 9 :(得分:2)

没有办法确定传递给像这样的函数的数组的大小

 void foo(int a[]);

在编译时或运行时没有足够的信息来解决它

sizeof技巧仅适用于指定数组大小的源位置

答案 10 :(得分:1)

数组引用将解决这个问题,虽然我从来没有建议使用过这样的东西(因为它只会在你总是通过ref传递,或者在定义了数组的范围内使用它时才会起作用):

#include <iostream>

template <typename Arrtype>
unsigned mysize (Arrtype (&arr)) {
 return sizeof(arr) / sizeof(arr[0]);
}

int main () {
 unsigned arr[13];
 std::cout << mysize(arr) << std::endl; // prints 13
}

此处还提到:http://cplusplus.co.il/2009/09/06/more-on-arrays/

编辑:正如评论中所建议的那样,另一种可能的解决方案是:

template <typename T, unsigned N>
unsigned mysize(T (&)[N]) {
    return N;
} 

答案 11 :(得分:1)

用作函数参数的“int a []”语法等同于“int * a”。

此语法:

  

int arr [] = {3,4,5,6,1,7,2};

仅在编译时工作。

所以你可以写:     int arr [] = {3,4,5,6,1,7,2};     printf(“size =%d \ n”,sizeof(arr));

这只能在编译时使用。

解决这个问题的一种方法是使数组动态化并创建自己的数组类型。

e.g。

typedef struct array{
int length;
int *arr;
} array;

使用malloc设置它的大小。您可以使用elipses填充它。

populate_array(array, ...);

e.g。然后调用populate_array(arr,1,2,3,4,5,6); 见stdarg。

但是,因为你的C编译器很可能也是一个C ++编译器。考虑使用std :: vector代替。然后,为你完成了艰苦的工作。

答案 12 :(得分:1)

我发现这是我们的C编译器制作的陷阱! (那么它是CPL的标准吗?C99还是其他什么?) 我在x86平台上的visual studio 2010中编写了以下代码。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[3] = { 0xa, 0xb, 0xc }; // Declare an array which include 3 items.
    int len = sizeof(a) / sizeof(a[0]); // And the variable len refer to the code like @Neil Chowdhury o_O
    int * pa0 = a; // Then i declared a pointer point to the first element to the source array a.
    int len2 = sizeof(a) / sizeof(pa0[0]); // Len2 refer to the same action like previous statement.
    int len3 = sizeof(a) / sizeof(pa0); // Len3 refer to a's size(one int's size)divide may be a int pointer's size.
    int len4 = sizeof(pa0); // Len4 is the size of int pointer pa0.
    int len5 = sizeof(a); // And len5 is the size of pa0, the same theory as previous stmt represented.
    int len6 = sizeof(a[0]); // Len6 equal to sizeof(int)
    int len7 = sizeof(pa0[0]); // Len7 equal to sizeof(int) too.
    return 0;
}

然后我构建了它们,并显示了反汇编代码。结果如下:

    int a[3] = { 0xa, 0xb, 0xc };
00D13108  mov         dword ptr [ebp-14h],0Ah  
00D1310F  mov         dword ptr [ebp-10h],0Bh  
00D13116  mov         dword ptr [ebp-0Ch],0Ch  
    int len = sizeof(a) / sizeof(a[0]);
00D1311D  mov         dword ptr [ebp-20h],3  // caution!
    int * pa0 = a;
00D13124  lea         eax,[ebp-14h]  
00D13127  mov         dword ptr [ebp-2Ch],eax  
    int len2 = sizeof(a) / sizeof(pa0[0]);
00D1312A  mov         dword ptr [ebp-38h],3   // caution!
    int len3 = sizeof(a) / sizeof(pa0);
00D13131  mov         dword ptr [ebp-44h],3   // caution!
    int len4 = sizeof(pa0);
00D13138  mov         dword ptr [ebp-50h],4  
    int len5 = sizeof(a);
00D1313F  mov         dword ptr [ebp-5Ch],0Ch  
    int len6 = sizeof(a[0]);
00D13146  mov         dword ptr [ebp-68h],4  
    int len7 = sizeof(pa0[0]);
00D1314D  mov         dword ptr [ebp-74h],4  
    return 0;
00D13154  xor         eax,eax  

那么,什么?看看这些“谨慎”的标记线! 我们的c编译器是如何处理我们的C代码的? 当指针到来时,特别是当我们写一个像这样的句子时 something = sizeof(数组的一个元素)/ sizeof(该数组的第一个元素) 编译器将我们的语法解析为数组的编号! 它尚未完成。

我也测试了一个“动态阵列”。由malloc等分配的数组。

#include <stdio.h>
#include <stdlib.h>

int main()
{

    int * a = NULL;
    int len = 0;
    a = (int *) calloc(7, sizeof(int));
    len = sizeof(a) / sizeof(a[0]);
    return 0;
}

构建..反汇编......

    int * a = NULL;
013830FE  mov         dword ptr [a],0  
    int len = 0;
01383105  mov         dword ptr [len],0  
    a = (int *) calloc(7, sizeof(int)); 
0138310C  mov         esi,esp  
0138310E  push        4  
01383110  push        7  
01383112  call        dword ptr [__imp__calloc (13882CCh)]  
01383118  add         esp,8  
0138311B  cmp         esi,esp  
0138311D  call        @ILT+300(__RTC_CheckEsp) (1381131h)  
01383122  mov         dword ptr [a],eax  
    len = sizeof(a) / sizeof(a[0]);
01383125  mov         dword ptr [len],1  // Hey!
    return 0;
0138312C  xor         eax,eax  
}
你知道吗?编译器此时错过了我们的语法。 所以结论是: 当你写一个特别喜欢的句子时: something = sizeof(数组的一个元素)/ sizeof(该数组的第一个元素) 编译器将理解并解析我们的代码,并将数组容量签名。仅当源数组是固定数组,或者数组之前已声明大小时。 (此时,大小是常量,可以存储在编译器的变量表中。) 它基本上像@rmn说的那样。 这就是我发现的。可能毫无价值。

答案 13 :(得分:0)

这非常有效:

int A[5] = {10, 20, 30, 40, 3};
int length = sizeof(A) / sizeof(A[0]);  // prints 5