如何确定C语言中数组的大小?
也就是说,数组可以容纳多少元素?
如何确定C语言中数组的大小?
也就是说,数组可以容纳多少元素?
当前回答
我建议永远不要使用sizeof(即使可以使用sizeof)来获取数组的两种不同大小,以元素数或字节数为单位,这是我在这里展示的最后两种情况。对于这两种大小中的每一种,都可以使用下面所示的宏来提高安全性。这样做的原因是为了让维护者清楚地看到代码的意图,以及sizeof(ptr)和sizeof(arr)的第一眼差别(这样写并不明显),这样对于阅读代码的每个人来说,bug就很明显了。
TL; diana:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))
#define ARRAY_BYTES(arr) (sizeof(arr) + must_be_array(arr))
must_be_array(arr)(定义如下)是需要的-Wsizeof-pointer-div是bug(截至2020年4月):
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(arr) (!is_same_type((arr), &(arr)[0]))
#define must_be(e) \
( \
0 * (int)sizeof( \
struct { \
static_assert(e); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
#define must_be_array(arr) must_be(is_array(arr))
关于这个主题有一些重要的错误:https://lkml.org/lkml/2015/9/3/428
我不同意Linus提供的解决方案,即永远不要使用数组符号表示函数的形参。
我喜欢数组表示法作为指针被用作数组的文档。但这意味着需要应用万无一失的解决方案,这样就不可能编写出有bug的代码。
从数组中,我们有三个大小,我们可能想知道:
数组元素的大小 数组中元素的个数 数组在内存中使用的字节大小
数组元素的大小
第一个很简单,不管我们处理的是数组还是指针,都是一样的。
用法示例:
void foo(size_t nmemb, int arr[nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
Qsort()需要这个值作为它的第三个参数。
对于其他两个大小,也就是问题的主题,我们要确保我们处理的是一个数组,如果不是,就会中断编译,因为如果我们处理的是指针,就会得到错误的值。当编译失败时,我们将很容易看到我们处理的不是一个数组,而是一个指针,我们只需要使用一个变量或宏来编写代码,将数组的大小存储在指针后面。
数组中元素的个数
这个是最常见的,很多答案都提供了典型的宏ARRAY_SIZE:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
最新版本的编译器,如GCC 8,会在将此宏应用于指针时发出警告,因此它是安全的(在较旧的编译器中,还有其他方法可以使其安全)。
它的工作原理是用整个数组的字节大小除以每个元素的大小。
用法示例:
void foo(size_t nmemb)
{
char buf[nmemb];
fgets(buf, ARRAY_SIZE(buf), stdin);
}
void bar(size_t nmemb)
{
int arr[nmemb];
for (size_t i = 0; i < ARRAY_SIZE(arr); i++)
arr[i] = i;
}
如果这些函数没有使用数组,而是将它们作为参数,那么之前的代码将无法编译,因此不可能有错误(考虑到使用了最近的编译器版本,或者使用了其他一些技巧),我们需要用值替换宏调用:
void foo(size_t nmemb, char buf[nmemb])
{
fgets(buf, nmemb, stdin);
}
void bar(size_t nmemb, int arr[nmemb])
{
for (size_t i = nmemb - 1; i < nmemb; i--)
arr[i] = i;
}
数组在内存中使用的字节大小
ARRAY_SIZE通常用作前一种情况的解决方案,但这种情况很少被安全地编写,可能是因为它不太常见。
获取这个值的常用方法是使用sizeof(arr)。问题是:和前一个问题一样;如果你有一个指针而不是数组,你的程序将会发疯。
这个问题的解决方案涉及到使用与之前相同的宏,我们知道这是安全的(如果应用于指针,它会中断编译):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
它的工作原理非常简单:它取消了ARRAY_SIZE所做的除法,因此在数学上取消之后,您最终只得到一个sizeof(arr),但增加了ARRAY_SIZE结构的安全性。
用法示例:
void foo(size_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
Memset()需要这个值作为它的第三个参数。
和以前一样,如果数组作为参数(指针)接收,它将不会编译,并且我们必须用值替换宏调用:
void foo(size_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
更新(23/apr/2020): -Wsizeof-pointer-div有bug:
今天我发现GCC中的新警告只适用于宏定义在非系统头文件中的情况。如果你在安装在系统中的头文件中定义宏(通常是/usr/local/include/或/usr/include/) (#include <foo.h>),编译器将不会发出警告(我尝试了GCC 9.3.0)。
因此我们有#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))并想让它安全。我们将需要C2X static_assert()和一些GCC扩展:
#include <assert.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(arr) (!is_same_type((arr), &(arr)[0]))
#define Static_assert_array(arr) static_assert(is_array(arr))
#define ARRAY_SIZE(arr) \
({ \
Static_assert_array(arr); \
sizeof(arr) / sizeof((arr)[0]); \
})
现在ARRAY_SIZE()是完全安全的,因此它的所有导数都是安全的。
更新:libbsd提供__arraycount():
Libbsd在<sys/cdefs.h>中提供了宏__arraycount(),这是不安全的,因为它缺少一对括号,但我们可以自己添加这些括号,因此我们甚至不需要在标头中写入除法(为什么我们要复制已经存在的代码?)这个宏是在一个系统头文件中定义的,所以如果我们使用它,我们就被迫使用上面的宏。
#inlcude <assert.h>
#include <stddef.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(arr) (!is_same_type((arr), &(arr)[0]))
#define Static_assert_array(arr) static_assert(is_array(arr))
#define ARRAY_SIZE(arr) \
({ \
Static_assert_array(arr); \
__arraycount((arr)); \
})
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
有些系统在<sys/param.h>中提供了nitems(),有些系统同时提供了这两种方法。您应该检查您的系统,并使用您现有的系统,也许还可以使用一些预处理器条件来实现可移植性和对两者的支持。
更新:允许宏在文件范围内使用:
不幸的是,({})gcc扩展名不能在文件范围内使用。 为了能够在文件范围内使用宏,静态断言必须是 在sizeof(struct{})中。然后,将其乘以0以不受影响 结果。强制转换为(int)可能很适合模拟函数 返回(int)0(在这种情况下,它是不必要的,但它 对于其他事情是可重用的)。
另外,ARRAY_BYTES()的定义可以稍微简化一点。
#include <assert.h>
#include <stddef.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(arr) (!is_same_type((arr), &(arr)[0]))
#define must_be(e) \
( \
0 * (int)sizeof( \
struct { \
static_assert(e); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
#define must_be_array(arr) must_be(is_array(arr))
#define ARRAY_SIZE(arr) (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_BYTES(arr) (sizeof(arr) + must_be_array(arr))
注:
这段代码使用了以下扩展,这些扩展是完全必要的,它们的存在对于实现安全是绝对必要的。如果你的编译器没有它们,或者一些类似的,那么你就不能达到这个级别的安全。
__builtin_types_compatible_p () typeof ()
我还使用了以下C2X功能。然而,如果使用旧的标准,它的缺失可以通过一些肮脏的技巧来克服(例如:什么是“::!!”)(在C11中你也有static_assert(),但它需要一个消息)。
static_assert ()
其他回答
如果你正在处理没有作为参数接收的数组,sizeof方式是正确的方式。作为参数发送给函数的数组被视为指针,因此sizeof将返回指针的大小,而不是数组的大小。
因此,在函数内部,此方法不起作用。相反,始终传递一个额外的参数size_t size,指示数组中元素的数量。
测试:
#include <stdio.h>
#include <stdlib.h>
void printSizeOf(int intArray[]);
void printLength(int intArray[]);
int main(int argc, char* argv[])
{
int array[] = { 0, 1, 2, 3, 4, 5, 6 };
printf("sizeof of array: %d\n", (int) sizeof(array));
printSizeOf(array);
printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
printLength(array);
}
void printSizeOf(int intArray[])
{
printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}
void printLength(int intArray[])
{
printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}
输出(64位Linux操作系统):
sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
输出(32位windows操作系统):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
最简单的答案:
#include <stdio.h>
int main(void) {
int a[] = {2,3,4,5,4,5,6,78,9,91,435,4,5,76,7,34}; // For example only
int size;
size = sizeof(a)/sizeof(a[0]); // Method
printf("size = %d", size);
return 0;
}
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
执行概要:
int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
完整的回答:
要确定数组的字节大小,可以使用sizeof 接线员:
int a[17];
size_t n = sizeof(a);
在我的电脑上,int是4字节长,所以n是68。
要确定数组中元素的数量,我们可以除法 数组的总大小乘以数组元素的大小。 你可以这样处理类型,像这样:
int a[17];
size_t n = sizeof(a) / sizeof(int);
并得到正确的答案(68 / 4 = 17)但如果类型 如果你忘记改变,你会有一个讨厌的bug sizeof(int)也是。
因此首选除数是sizeof(a[0])或等效的sizeof(*a),即数组第一个元素的大小。
int a[17];
size_t n = sizeof(a) / sizeof(a[0]);
另一个优点是您现在可以轻松地参数化 宏中的数组名,get:
#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
int a[17];
size_t n = NELEMS(a);
你可以使用&操作符。以下是源代码:
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[10];
int *p;
printf("%p\n", (void *)a);
printf("%p\n", (void *)(&a+1));
printf("---- diff----\n");
printf("%zu\n", sizeof(a[0]));
printf("The size of array a is %zu\n", ((char *)(&a+1)-(char *)a)/(sizeof(a[0])));
return 0;
};
下面是示例输出
1549216672
1549216712
---- diff----
4
The size of array a is 10