C语言中是否有一个标准函数,可以返回数组的长度?
通常其他答案中描述的技术被封装在宏中,以便让其更易于阅读。类似于:
#define COUNT_OF( arr) (sizeof(arr)/sizeof(0[arr]))
请注意,上面的宏使用了一个小技巧,在索引操作符('[]
')中放置了数组名,而不是0
- 这样做是为了防止在C++代码中错误地使用宏并出现重载operator[]()
的情况。编译器将会发出警告而不是给出错误结果。#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
在这个版本中,如果错误地将指针传递为参数,则编译器在某些情况下会发出警告 - 具体来说,如果指针的大小不能被指针所指向的对象的大小整除,那么除零操作将导致编译器报错。事实上,我使用过的至少一个编译器会给出一个警告而不是一个错误 - 我不确定对于具有除以零表达式的表达式它生成什么内容。winnt.h
中使用的一种相当复杂的基于模板的方法。0[arr]
能够作为 arr[0]
来使用吗? - meguliarr[0]
可以变成*(arr + 0)
,而0[arr]
则变成了*(0 + arr)
,它们当然是等价的。 - Cameronstd::size
函数 - https://en.cppreference.com/w/cpp/iterator/size - undefined没有。
对于固定大小的数组,您可以使用Andrew提到的常见技巧,sizeof(array) / sizeof(array[0])
- 但这仅适用于声明数组的范围内。
sizeof(array)
给出整个数组的大小,而 sizeof(array[0])
给出第一个元素的大小。
请参阅Michael的答案以了解如何在宏中包装它。
对于动态分配的数组,您可以使用一个整数类型来跟踪大小,或者如果可能的话将其设置为0结尾(即分配1个额外元素,并将最后一个元素设置为0)。
sizeof(array)/sizeof(array [0])
仅适用于声明数组的同一作用域。我们无法将array
传递给另一个函数并期望代码能够正常工作。 - Chris Lutzsizeof
会在运行时计算。 - Johannes Schaub - litbsizeof array / sizeof array[0]
*array
来进一步缩短代码 :-) - Frerich Raabemalloc()
等)。它还无法处理函数参数列表中的“数组”——但它们本来就不是数组。 - Jonathan Leffler可以通过以下方式获取数组 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()
就可以访问长度和所有元素,而你不必担心数组/指针二分法。
这样做的额外优点是允许长度变化(即使用的元素数量,而不是分配的单位数)。但我不再使用这种方法,因为我认为两个参数的数组版本(大小和数据)更好。
union
技巧很巧妙,但我更愿意使用 struct
的技巧(或者同样思路的非技巧版本)。 - Chris Lutz#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()
,这个方法已经被提到了无数次,可以暂时规避问题。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);
将会从s1
复制n
个字节到s2
,这允许我们使用n
确保我们的缓冲区永远不会溢出。这是一个好策略-它的开销低,并且实际上生成了一些有效的代码(与strcpy()
相比较,其必须检查字符串的结尾并没有办法“知道”要进行多少次迭代,以及可怜的混乱的strncpy()
,它必须同时检查两个-都可能变慢,并且如果您已经为某种原因计算了字符串的长度,则可以通过使用memcpy()
加速任一个)。
另一种方法是将您的代码封装在一个struct
中。常见的做法如下:
typedef struct _arr {
size_t len;
int arr[0];
} arr;
arr *a = malloc(sizeof(*a) + sizeof(int) * 5);
a->len = 5;
然而,这只是一个相对明确的hack(C99允许您使用int arr[]
),而且相当费力。一种更好定义的方法是:
typedef struct _arr {
size_t len;
int *arr;
} arr;
offsetof(arr, arr) + n * sizeof(int)
(因此,数组声明中使用的大小无关紧要)。 - AnT stands with Russiaarr[0]
(伴随着不适)。不过我从未见过使用 offsetof()
计算大小,虽然这是一种不错的确保方法。而我则持不同意见。黑客应该大声喊出“这是一个黑客!请在你有机会时改进这段代码!” - Chris Lutzint arr[0]
不会有任何问题。它不可能是那么非法的。) - Chris Lutza
,则可以将数组中的元素数量表示为sizeof a / sizeof *a
。如果您允许您的数组对象衰减为指针类型(或者一开始只有一个指针对象),那么在一般情况下无法确定数组中的元素数量。