C中是否有一个标准函数可以返回数组的长度?

时间:2009-10-21 04:50:52

标签: c arrays

C中是否有一个标准函数可以返回数组的长度?

7 个答案:

答案 0 :(得分:64)

通常,其他答案中描述的技术被封装在一个宏中,以使眼睛更容易。类似的东西:

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

请注意,上面的宏使用了一个小技巧,即将数组名称放在索引运算符('[]')而不是0中 - 如果宏被错误地用于包含重载operator[]()的项的C ++代码。编译器会抱怨而不是给出错误的结果。

但是,请注意,如果您碰巧传递指针而不是数组,宏将默默地给出错误的结果 - 这是使用此技术的主要问题之一。

我最近开始使用我从Google Chromium的代码库中窃取的更复杂的版本:

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

在这个版本中如果错误地将指针作为参数传递,编译器会在某些情况下抱怨 - 特别是如果指针的大小不能被指针所指向的对象的大小整除。在这种情况下,除零将导致编译器出错。实际上至少有一个我用过的编译器给出了一个警告而不是一个错误 - 我不确定它为那个除以零的表达式生成了什么。

那个宏没有关闭错误地使用它的大门,但是它在我用直接C看到的时候非常接近。

如果您希望在使用C ++时获得更安全的解决方案,请查看Compile time sizeof_array without using a macro,其中介绍了Microsoft在winnt.h中使用的基于模板的相当复杂的方法。

答案 1 :(得分:14)

不,没有。

对于常量大小的数组,您可以使用Andrew提到的常见技巧sizeof(array) / sizeof(array[0]) - 但这仅适用于声明数组的范围。
sizeof(array)为您提供整个数组的大小,而sizeof(array[0])为您提供第一个元素的大小。
请参阅Michaels answer,了解如何将其包装在宏中。

对于动态分配的数组,您可以跟踪整数类型的大小,或者如果可能的话使其以0结尾(即分配1个元素并将最后一个元素设置为0)。

答案 2 :(得分:5)

sizeof array / sizeof array[0]

答案 3 :(得分:3)

数组x中的元素数量可以通过以下方式获得:

sizeof(x)/sizeof(x[0])

你需要知道,当传递给函数时,数组会降级为携带大小信息的指针。实际上,大小信息从不可用于运行时,因为它是在编译时计算的,但是您可以在数组可见的情况下(即,它尚未降级的情况下)可用)。

当我将数组传递给我需要视为数组的函数时,我总是确保传递两个参数:

  • 阵列的长度;和
  • 指向数组的指针。

因此,虽然可以将数组视为声明它的数组,但它在其他地方被视为大小和指针。

我倾向于使用以下代码:

#define countof(x) (sizeof(x)/sizeof(x[0]))
: : :
int numbers[10];
a = fn (countof(numbers),numbers);

然后fn()将获得大小信息。

我过去使用的另一个技巧(我认为有点麻烦,但我会在这里给它完整性)是有一个联合数组并使第一个元素成为长度,如:

typedef union {
    int len;
    float number;
} tNumber;
tNumber number[10];
: : :
number[0].len = 5;
a = fn (number);

然后fn()可以访问长度和所有元素,您不必担心数组/指针二分法。

这具有允许长度变化的附加优点(即,使用中的元素的数量,分配的单元的数量)。但我倾向于不再使用它,因为我认为两个参数的数组版本(大小和数据)更好。

答案 4 :(得分:2)

简单的答案当然是否定的。但实际的答案是“无论如何我都需要知道”,所以让我们讨论一下解决这个问题的方法。

如前所述,已经有一百万次提到它的一种方法是使用sizeof()

int i[] = {0, 1, 2};
...
size_t i_len = sizeof(i) / sizeof(i[0]);

这是有效的,直到我们尝试将i传递给函数,或者指向i。那么更一般的解决方案呢?

公认的通用解决方案是将数组长度与数组一起传递给函数。我们在标准库中看到了很多:

void *memcpy(void *s1, void *s2, size_t n);

n字节从s1复制到s2,允许我们使用n来确保缓冲区永不溢出。这是一个很好的策略 - 它具有较低的开销,并且它实际上生成了一些有效的代码(与strcpy()相比,它必须检查字符串的结尾并且无法“知道”它必须有多少次迭代make,和困惑的混淆strncpy(),它必须检查两者 - 可以更慢,如果你碰巧已经计算过,可以使用memcpy()加速由于某种原因,字符串的长度。)

另一种方法是将代码封装在struct中。常见的黑客是:

typedef struct _arr {
  size_t len;
  int arr[0];
} arr;

如果我们想要一个长度为5的数组,我们这样做:

arr *a = malloc(sizeof(*a) + sizeof(int) * 5);
a->len = 5;

但是,这是一个只有适度定义的黑客(C99允许你使用int arr[])并且相当劳力密集。 “更好定义”的方法是:

typedef struct _arr {
  size_t len;
  int *arr;
} arr;

然后我们的分配(和解除分配)变得更加复杂。当然,这些方法中的任何一个的好处是,现在制作的数组将随身携带。它的内存效率略低,但它非常安全。如果您选择其中一个路径,请务必编写辅助函数,这样您就不必手动分配和释放(并使用)这些结构。

答案 5 :(得分:0)

如果您有一个数组类型的对象a,则数组中的元素数可以表示为sizeof a / sizeof *a。如果你允许你的数组对象衰减到指针类型(或只有一个指针对象开始),那么一般情况下无法确定数组中元素的数量。

答案 6 :(得分:0)

我创建了一个宏,该宏返回数组的大小,但是如果在指针上使用它会产生编译器错误。但是请注意,它依赖于gcc扩展。因此,它不是便携式解决方案。

#define COUNT(a) (__builtin_choose_expr( \
                  __builtin_types_compatible_p(typeof(a), typeof(&(a)[0])), \
                  (void)0, \
                  (sizeof(a)/sizeof((a)[0]))))

int main(void)
{
    int arr[5];
    int *p;
    int x = COUNT(arr);
//  int y = COUNT(p);
}

如果您删除评论,则会产生:error: void value not ignored as it ought to be