繁体   English   中英

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

[英]Is there a standard function in C that would return the length of an array?

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

通常,其他答案中描述的技术被封装在一个宏中,以使其更容易理解。 就像是:

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

请注意,上面的宏使用了一个小技巧,将数组名称放入索引运算符 (' [] ') 而不是0 - 这样做是为了防止宏在 C++ 代码中被错误地用于带有重载operator[]() . 编译器会抱怨而不是给出一个糟糕的结果。

但是,还要注意,如果您碰巧传递的是指针而不是数组,宏将默默地给出一个糟糕的结果——这是使用这种技术的主要问题之一。

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

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

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

该宏并没有关闭错误使用它的大门,但它与我在直 C 中见过的一样接近。

如果您在使用 C++ 时想要更安全的解决方案,请查看不使用宏的编译时 sizeof_array ,该宏描述了 Microsoft 在winnt.h使用的相当复杂的基于模板的方法。

不,那里没有。

对于恒定大小的数组,您可以使用 Andrew 提到的常用技巧sizeof(array) / sizeof(array[0]) - 但这仅在声明数组的范围内有效。
sizeof(array)为您提供整个数组的大小,而sizeof(array[0])为您提供第一个元素的大小。
有关如何将其包装在宏中的信息,请参阅Michaels 的回答

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

sizeof array / sizeof array[0]

可以通过以下方式获得数组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()可以访问长度和所有元素,您不必担心数组/指针二分法。

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

我创建了一个返回数组大小的宏,但如果在指针上使用会产生编译器错误。 但是请注意,它依赖于 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

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

正如已经提到的大约一百万次那样, sizeof()摆脱它的一种方法是使用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() ,它必须检查字符串的结尾并且无法“知道”它必须进行多少次迭代,和可怜的混淆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;

然而,这是一个只有适度明确定义的 hack(C99 允许您使用int arr[] )并且相当费力。 一个“更好定义”的方法是:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM