ChatGPT解决这个技术问题 Extra ChatGPT

如何确定 C 中数组的大小?

如何确定 C 中数组的大小?

也就是数组能容纳多少个元素?


S
S.S. Anne

执行摘要:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

完整答案:

要确定数组的大小(以字节为单位),您可以使用 sizeof 运算符:

int a[17];
size_t n = sizeof(a);

在我的电脑上,整数是 4 个字节长,所以 n 是 68。

要确定数组中元素的数量,我们可以将数组的总大小除以数组元素的大小。您可以使用类型执行此操作,如下所示:

int a[17];
size_t n = sizeof(a) / sizeof(int);

并得到正确的答案 (68 / 4 = 17),但如果 a 的类型发生了变化,如果您忘记更改 sizeof(int),您将遇到一个令人讨厌的错误。

所以首选除数是 sizeof(a[0]) 或等效的 sizeof(*a),即数组第一个元素的大小。

int a[17];
size_t n = sizeof(a) / sizeof(a[0]);

另一个优点是您现在可以轻松地在宏中参数化数组名称并获得:

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

int a[17];
size_t n = NELEMS(a);

生成的代码将是相同的,因为编译器在编译时知道 *int_arr 的类型(因此知道 sizeof(*int_arr) 的值)。这将是一个常数,编译器可以相应地进行优化。
所有编译器都应该是这种情况,因为 sizeof 的结果被定义为编译时常量。
重要提示:不要停止阅读这里,阅读下一个答案!这仅适用于堆栈上的数组,例如,如果您使用 malloc() 或访问函数参数,那么您就不走运了。见下文。
对于 C 或 C++ 中的 Windows API 编程,在 WinNT.h 中定义了 ARRAYSIZE makro(它被其他头文件拉入)。所以 WinAPI 用户不需要定义自己的宏。
@Markus 它适用于任何具有数组类型的变量;这不必“在堆栈上”。例如 static int a[20]; 。但是您的评论对可能没有意识到数组和指针之间区别的读者很有用。
E
Elideb

sizeof 方式是您处理未作为参数接收的数组的正确方式 iff。作为参数发送给函数的数组被视为指针,因此 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

如果只传递了指向第一个数组元素的指针,为什么是 length of parameter:2
@Bbvarghe 那是因为 64 位系统中的指针是 8 个字节(sizeof(intArray)),但整数仍然(通常)是 4 个字节长(sizeof(intArray[0]))。
@Pacerier:没有正确的代码-通常的解决方案是将长度与数组一起作为单独的参数传递。
等等,所以没有办法直接从指针访问数组并查看它的大小?这里是 C 的新手。
@Michael Trouw:如果 is 让您感觉更好,您可以使用运算符语法:(sizeof array / sizeof *array)
M
Magnus Hoff

值得注意的是,sizeof 在处理已衰减为指针的数组值时无济于事:即使它指向数组的开头,但对于编译器来说,它与指向单个元素的指针相同那个数组。指针不会“记住”有关用于初始化它的数组的任何其他内容。

int a[10];
int* p = a;

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));

@ Magnus:标准将 sizeof 定义为产生对象中的字节数,并且 sizeof (char) 始终为一。字节中的位数是特定于实现的。编辑:ANSI C++ 标准第 5.3.3 节 Sizeof:“sizeof 运算符在其操作数的对象表示中产生字节数。[...] sizeof (char)、sizeof (signed char) 和 sizeof (unsigned char) 是1; sizeof 应用于任何其他基本类型的结果是实现定义的。”
第 1.6 节 C++ 内存模型:“C++ 内存模型中的基本存储单元是字节。一个字节至少大到足以包含基本执行字符集的任何成员,并且由连续的位序列组成,数字其中是实现定义的。”
我记得 CRAY 的 C 是 32 位的 char。所有标准都说可以表示从 0 到 127 的整数值,其范围至少是 -127 到 127(char 是有符号的)或 0 到 255(char 是无符号的)。
这是一个很好的回应。我想评论一下,上述所有断言都被评估为 TRUE。
N
Neuron

sizeof“技巧”是我所知道的最好的方式,在括号的使用上做了一个小但(对我来说,这是一个主要的烦恼)重要的变化。

正如 Wikipedia 条目所表明的那样,C 的 sizeof 不是函数;它是一个运算符。因此,它不需要在其参数周围加上括号,除非参数是类型名称。这很容易记住,因为它使参数看起来像一个强制转换表达式,它也使用括号。

所以:如果您有以下情况:

int myArray[10];

您可以使用如下代码找到元素的数量:

size_t n = sizeof myArray / sizeof *myArray;

对我来说,这比带括号的替代方案更容易阅读。我也赞成在除法的右侧使用星号,因为它比索引更简洁。

当然,这也是编译时的,所以不用担心分割影响程序的性能。因此,请尽可能使用此表格。

当您有一个实际对象时,最好在实际对象上使用 sizeof,而不是在类型上使用,因为这样您就不必担心会出错并声明错误的类型。

例如,假设您有一个函数将一些数据作为字节流输出,例如通过网络输出。让我们调用函数 send(),并将其作为参数,指向要发送的对象的指针,以及对象中的字节数。因此,原型变为:

void send(const void *object, size_t size);

然后你需要发送一个整数,所以你把它编码成这样:

int foo = 4711;
send(&foo, sizeof (int));

现在,您通过在两个地方指定 foo 的类型,介绍了一种巧妙的射击自己的方法。如果一个改变而另一个没有改变,代码就会中断。因此,总是这样做:

send(&foo, sizeof foo);

现在你受到保护了。当然,您复制了变量的名称,但是如果您更改它,这很可能会破坏编译器可以检测到的方式。


顺便说一句,它们在处理器级别是相同的指令吗? sizeof(int) 是否需要比 sizeof(foo) 更少的指令?
@Pacerier:不,它们是相同的。想想 int x = 1+1;int x = (1+1);。在这里,括号纯粹是纯粹的审美。
@Aidiakapi 那不是真的,请考虑 C99 VLA。
sizeof 可能是一个运算符,但根据 Linus Torvalds,它应该被视为一个函数。我同意。在这里阅读他的理性:lkml.org/lkml/2012/7/11/103
为什么要省略括号使其更具可读性?例如,sizeof myArray / sizeof *myArray; 可能意味着 sizeof(myArray / sizeof *myArray);。我知道这没有意义,但还是明确的恕我直言。
A
Arjun Sreedharan
int size = (&arr)[1] - arr;

查看 this link 以了解说明


小挑剔:指针减法的结果具有类型 ptrdiff_t。 (通常在 64 位系统上,这将是比 int 更大的类型)。即使您在此代码中将 int 更改为 ptrdiff_t,如果 arr 占用超过一半的地址空间,它仍然存在错误。
@MM 另一个小问题:根据您的系统架构,地址空间并不像大多数系统上的指针大小那么大。例如,Windows 将 64 位应用程序的地址空间限制为 8TB 或 44 位。因此,即使您的数组大于地址空间的一半(例如 4.1TB),它也不会是错误。只有当您的地址空间在这些系统上超过 63 位时,才有可能遇到此类错误。一般来说,不用担心。
@Aidiakapi 在 32 位 x86 Linux 或带有 /3G 选项的 Windows 上,您有 3G/1G 用户/内核拆分,这允许您将数组大小增加到地址空间大小的 75%。
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1]; 视为全局变量。 buf3[] 声明失败,因为 (&buf1)[1] - buf1 不是常量。
这在技术上是未定义的行为,因为标准明确禁止在数组末尾取消引用(即使您不尝试读取存储的值)
a
alx

我建议永远不要使用 sizeof(即使可以使用)来获取数组的两种不同大小中的任何一种,无论是元素数还是字节数,这是我在这里展示的最后两种情况.对于这两种尺寸中的每一种,都可以使用下面显示的宏来使其更安全。原因是为了让维护者清楚代码的意图,以及乍看之下 sizeof(ptr)sizeof(arr) 的区别(这样写并不明显),这样每个阅读代码的人都会发现 bug。< /强>

TL;博士:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))
#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)    (sizeof(arr) + must_be_array(arr))
#define ARRAY_SBYTES(arr)   ((ssize_t)ARRAY_BYTES(arr))

must_be_array(arr)(定义如下)需要作为 -Wsizeof-pointer-div is buggy(截至 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 提供的解决方案,即永远不要对函数的参数使用数组表示法。

我喜欢数组表示法作为将指针用作数组的文档。但这意味着需要应用一个万无一失的解决方案,这样就不可能编写有错误的代码。

从一个数组中,我们可能想知道三种大小:

数组元素的大小

数组中的元素个数

数组在内存中使用的字节大小

数组元素的大小

第一个非常简单,我们处理的是数组还是指针都没有关系,因为它的完成方式相同。

使用示例:

void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
        qsort(arr, nmemb, sizeof(arr[0]), cmp);
}

qsort() 需要这个值作为它的第三个参数。

对于其他两个大小,这是问题的主题,我们要确保我们正在处理一个数组,如果不是,则中断编译,因为如果我们正在处理一个指针,我们将得到错误的值.当编译被破坏时,我们将能够很容易地看到我们不是在处理数组,而是在处理一个指针,我们只需要使用一个变量或一个存储大小的宏来编写代码指针后面的数组。

数组中的元素个数

这个是最常见的,很多答案都为你提供了典型的宏ARRAY_SIZE

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

鉴于 ARRAY_SIZE 的结果通常与 ptrdiff_t 类型的有符号变量一起使用,因此最好定义此宏的有符号变体:

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

具有超过 PTRDIFF_MAX 个成员的数组将为这个签名版本的宏提供无效值,但从读取 C17::6.5.6.9 来看,这样的数组已经在玩火了。在这些情况下,只应使用 ARRAY_SIZEsize_t

最新版本的编译器,例如 GCC 8,会在您将此宏应用于指针时发出警告,因此它是安全的(还有其他方法可以使其与旧编译器一起安全)。

它通过将整个数组的字节大小除以每个元素的大小来工作。

使用示例:

void foo(ptrdiff_t nmemb)
{
        char buf[nmemb];

        fgets(buf, ARRAY_SIZE(buf), stdin);
}

void bar(ptrdiff_t nmemb)
{
        int arr[nmemb];

        for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
                arr[i] = i;
}

如果这些函数不使用数组,而是将它们作为参数获取,则以前的代码将无法编译,因此不可能有错误(假设使用了最新的编译器版本,或者使用了其他技巧) ,我们需要用值替换宏调用:

void foo(ptrdiff_t nmemb, char buf[nmemb])
{
        fgets(buf, nmemb, stdin);
}

void bar(ptrdiff_t nmemb, int arr[nmemb])
{
        for (ptrdiff_t i = 0; i < nmemb; i++)
                arr[i] = i;
}

数组在内存中使用的字节大小

ARRAY_SIZE 通常用作前一种情况的解决方案,但这种情况很少安全地编写,可能是因为它不太常见。

获取此值的常用方法是使用 sizeof(arr)。问题:和上一个一样;如果你有一个指针而不是一个数组,你的程序就会发疯。

该问题的解决方案涉及使用与以前相同的宏,我们知道它是安全的(如果将其应用于指针,它会破坏编译):

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

鉴于有时将 ARRAY_BYTES 的结果与返回 ssize_t 的函数的输出进行比较,最好定义此宏的有符号变体:

#define ARRAY_SBYTES(arr)       ((ssize_t)ARRAY_BYTES(arr))

它的工作原理非常简单:它取消了 ARRAY_SIZE 所做的除法,因此在数学取消后,您最终只得到一个 sizeof(arr),但 ARRAY_SIZE 构造增加了安全性。

使用示例:

void foo(ptrdiff_t nmemb)
{
        int arr[nmemb];

        memset(arr, 0, ARRAY_BYTES(arr));
}

memset() 需要这个值作为它的第三个参数。

和以前一样,如果数组作为参数(指针)被接收,它将无法编译,我们将不得不用值替换宏调用:

void foo(ptrdiff_t nmemb, int arr[nmemb])
{
        memset(arr, 0, sizeof(arr[0]) * nmemb);
}

更新(2020 年 4 月 23 日):-Wsizeof-pointer-div 有问题:

今天我发现 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 扩展:Statements and Declarations in Expressions__builtin_types_compatible_p

#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_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))
#define ARRAY_SBYTES(arr)       ((ssize_t)ARRAY_BYTES(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_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof(arr) + must_be_array(arr))
#define ARRAY_SBYTES(arr)       ((ssize_t)ARRAY_BYTES(arr))

笔记:

此代码使用以下扩展,这些扩展是完全必要的,并且它们的存在对于实现安全是绝对必要的。如果你的编译器没有它们,或者一些类似的,那么你就无法达到这种安全级别。

__builtin_types_compatible_p()

类型()

我还使用了以下 C2X 功能。但是,可以使用一些肮脏的技巧来克服使用旧标准的缺失(参见例如:What is “:-!!” in C code?)(在 C11 中您也有 static_assert(),但它需要一条消息)。

静态断言()


你介意解释一下为什么投反对票吗?这显示了对其他地方未显示的不安全且常见的构造 (sizeof(arr)) 的解决方案:ARRAY_BYTES(arr)
ARRAY_SIZE 很常见,可以自由使用,ARRAY_BYTES 的名称非常明确,应该在 ARRAY_SIZE 旁边定义,这样用户就可以轻松看到两者,而且通过它的用法,我认为阅读代码的任何人都不会怀疑什么确实如此。我的意思是不要使用简单的 sizeof,而是使用这种结构;如果你想每次都写这些结构,你可能会犯错误(如果你复制粘贴很常见,如果你每次都写它们也很常见,因为它们有很多括号)......
...,所以我站在主要结论上:单个 sizeof 显然是不安全的(原因在答案中),并且每次不使用宏而是使用我提供的结构更加不安全,所以唯一的要走的路是宏。
@MarkHarrison 我确实知道指针和数组之间的区别。但是有时我有一个函数,后来我将它重构为小函数,首先是一个数组,后来是一个指针,这就是如果你忘记更改 sizeof 的地方,你搞砸了,很容易看不到其中之一。
@hyde 另外,我知道区别并不意味着每个人都知道区别,为什么不使用基本上消除 100% 的错误的东西呢?那个 bug 几乎进入了 Linux。它到达了Linus,这意味着它通过了很多审查,这也意味着同样的错误有可能在内核的另一部分进入Linux,正如他所说。
J
Joncom

您可以使用 sizeof 运算符,但它不适用于函数,因为它将获取指针的引用,您可以执行以下操作来查找数组的长度:

len = sizeof(arr)/sizeof(arr[0])

最初在这里找到的代码:C program to find the number of elements in an array


P
Peter Mortensen

如果你知道数组的数据类型,你可以使用类似的东西:

int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};

int noofele = sizeof(arr)/sizeof(int);

或者如果你不知道数组的数据类型,你可以使用类似的东西:

noofele = sizeof(arr)/sizeof(arr[0]);

注意:只有在运行时未定义数组(如 malloc)并且数组未在函数中传递时,此操作才有效。在这两种情况下,arr(数组名)都是一个指针。


int noofele = sizeof(arr)/sizeof(int); 仅比编码 int noofele = 9; 好一半。如果数组大小发生变化,使用 sizeof(arr) 可以保持灵活性。然而,如果 arr[] 的类型发生变化,sizeof(int) 需要更新。最好使用 sizeof(arr)/sizeof(arr[0]),即使类型是众所周知的。不清楚为什么将 int 用于 noofelesize_t,即 sizeof() 返回的类型。
P
Peter Mortensen

每个人都在使用的宏 ARRAYELEMENTCOUNT(x) 的计算结果不正确。实际上,这只是一个敏感问题,因为您不能拥有导致“数组”类型的表达式。

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))

ARRAYELEMENTCOUNT(p + 1);

实际上评估为:

(sizeof (p + 1) / sizeof (p + 1[0]));

然而

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])

ARRAYELEMENTCOUNT(p + 1);

它正确评估为:

(sizeof (p + 1) / sizeof (p + 1)[0]);

这实际上与数组的大小没有太大关系。我刚刚注意到很多错误,因为没有真正观察 C 预处理器是如何工作的。您总是包装宏参数,而不是可能涉及的表达式。

这是对的;我的例子很糟糕。但这实际上正是应该发生的事情。正如我之前提到的,p + 1 最终将成为指针类型并使整个宏无效(就像您尝试在带有指针参数的函数中使用宏一样)。

归根结底,在这种特殊情况下,错误并不重要(所以我只是在浪费大家的时间;huzzah!),因为您没有具有“数组”类型的表达式。但我认为关于预处理器评估细节的这一点真的很重要。


感谢您的解释。原始版本会导致编译时错误。 Clang 报告“下标值不是数组、指针或向量”。在这种情况下,这似乎是更可取的行为,尽管您对宏中的评估顺序的评论很好。
我没有将编译器投诉视为错误类型的自动通知。谢谢!
有理由不使用 (sizeof (x) / sizeof (*x)) 吗?
A
Andreas Spindler

对于多维数组,它有点复杂。通常人们定义明确的宏常量,即

#define g_rgDialogRows   2
#define g_rgDialogCols   7

static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
    { " ",  " ",    " ",    " 494", " 210", " Generic Sample Dialog", " " },
    { " 1", " 330", " 174", " 88",  " ",    " OK",        " " },
};

但是这些常量也可以在编译时使用 sizeof 进行评估:

#define rows_of_array(name)       \
    (sizeof(name   ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name)    \
    (sizeof(name[0]) / sizeof(name[0][0]))

static char* g_rgDialog[][7] = { /* ... */ };

assert(   rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);

请注意,此代码适用于 C 和 C++。对于二维以上的数组,请使用

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

等等,无穷无尽。


P
Peter Mortensen

C中数组的大小:

int a[10];
size_t size_of_array = sizeof(a);      // Size of array a
int n = sizeof (a) / sizeof (a[0]);    // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a                                          
                                       // Size of each element = size of type

好奇代码使用了 size_t size_of_elementintint n = sizeof (a) / sizeof (a[0]); 而不是 size_t n = sizeof (a) / sizeof (a[0]);
嗨@Yogeesh HT,你能回答 chux 的疑问吗?我也很想知道 int n=sizeof(a)/sizeof(a[0]) 如何给出数组的长度以及为什么我们不使用 size_t 作为数组的长度。任何人都可以回答吗?
@Brain sizeof(a) 给出数组中所有元素的 sizeof (a[0]) 给出第一个元素的 sizeof。假设 a = {1,2,3,4,5}; sizeof(a) = 20bytes (如果 sizeof(int)= 4bytes 乘以 5), sizeof(a[0]) = 4bytes, 所以 20/4 = 5 即没有元素
@YogeeshHT 对于像 char a[INT_MAX + 1u]; 这样的非常大的数组,在 int n = sizeof (a) / sizeof (a[0]); 中使用的 int n 是不够的(它是 UB)。使用 size_t n = sizeof (a) / sizeof (a[0]); 不会导致此问题。
T
T Percival
sizeof(array) / sizeof(array[0])

根据 array 的类型,如果 arraycharunsigned charsigned char 的数组,则不需要使用 sizeof(array) / sizeof(array[0]) - 引用自 C18,6.5.3.4/4:< i>“当 sizeof 应用于类型为 char、unsigned char 或 signed char(或其限定版本)的操作数时,结果为 1。” 在这种情况下,您可以简单地执行 sizeof(array) 为在我的专用 answer 中进行了解释。
P
Peter Mortensen

“你介绍了一种用脚射击自己的微妙方式”

'native' 数组不存储它们的大小。因此建议将数组的长度保存在单独的变量/常量中,并在传递数组时传递它,即:

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

你应该总是避免使用原生数组(除非你不能,在这种情况下,请注意你的脚)。如果您正在编写 C++,请使用 STL 的“向量”容器。 “与数组相比,它们提供几乎相同的性能”,而且它们更有用!

// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;  

// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
    numbers.push_back(i);

// Determine the size of the array
cout << numbers.size();

请参阅:http://www.cplusplus.com/reference/stl/vector/


我读过在 C 中声明整数常量的正确方法是使用 enum 声明。
问题是关于 C,而不是 C++。所以没有STL。
A
Andy Nugent
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

请注意,这仅适用于实际数组,不适用于恰好指向数组的指针。
b
bartimar

如果你真的想这样做来传递你的数组,我建议实现一个结构来存储一个指向你想要一个数组的类型的指针和一个表示数组大小的整数。然后你可以将它传递给你的函数。只需将数组变量值(指向第一个元素的指针)分配给该指针。然后您可以使用 Array.arr[i] 获取第 i 个元素并使用 Array.size 获取数组中的元素数。

我为您提供了一些代码。它不是很有用,但您可以使用更多功能对其进行扩展。老实说,如果这些是你想要的,你应该停止使用 C 并使用另一种内置这些特性的语言。

/* Absolutely no one should use this...
   By the time you're done implementing it you'll wish you just passed around
   an array and size to your functions */
/* This is a static implementation. You can get a dynamic implementation and 
   cut out the array in main by using the stdlib memory allocation methods,
   but it will work much slower since it will store your array on the heap */

#include <stdio.h>
#include <string.h>
/*
#include "MyTypeArray.h"
*/
/* MyTypeArray.h 
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/
typedef struct MyType
{
   int age;
   char name[20];
} MyType;
typedef struct MyTypeArray
{
   int size;
   MyType *arr;
} MyTypeArray;

MyType new_MyType(int age, char *name);
MyTypeArray newMyTypeArray(int size, MyType *first);
/*
#endif
End MyTypeArray.h */

/* MyTypeArray.c */
MyType new_MyType(int age, char *name)
{
   MyType d;
   d.age = age;
   strcpy(d.name, name);
   return d;
}

MyTypeArray new_MyTypeArray(int size, MyType *first)
{
   MyTypeArray d;
   d.size = size;
   d.arr = first;
   return d;
}
/* End MyTypeArray.c */


void print_MyType_names(MyTypeArray d)
{
   int i;
   for (i = 0; i < d.size; i++)
   {
      printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);
   }
}

int main()
{
   /* First create an array on the stack to store our elements in.
      Note we could create an empty array with a size instead and
      set the elements later. */
   MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};
   /* Now create a "MyTypeArray" which will use the array we just
      created internally. Really it will just store the value of the pointer
      "arr". Here we are manually setting the size. You can use the sizeof
      trick here instead if you're sure it will work with your compiler. */
   MyTypeArray array = new_MyTypeArray(2, arr);
   /* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
   print_MyType_names(array);
   return 0;
}

无法对执行 strcpy(d.name, name); 且未处理溢出的代码进行投票。
P
Paulo Pinheiro

最好的方法是保存这些信息,例如,在一个结构中:

typedef struct {
     int *array;
     int elements;
} list_s;

实现所有必要的功能,例如创建、销毁、检查相等性以及您需要的所有其他功能。作为参数传递更容易。


int elementssize_t elements 的任何原因?
K
Keivan

函数 sizeof 返回数组在内存中使用的字节数。如果要计算数组中的元素数,应将该数除以数组的 sizeof 变量类型。假设 int array[10];,如果您计算机中的变量类型整数是 32 位(或 4 个字节),为了获得数组的大小,您应该执行以下操作:

int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);

A
Azatik1000

一个更优雅的解决方案将是

size_t size = sizeof(a) / sizeof(*a);

M
M.M

您可以使用 & 运算符。这是源代码:

#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

我没有投反对票,但这就像用砖头敲钉子一样,因为您没有注意到旁边放着一把锤子。此外,人们倾向于不喜欢使用未初始化的变量......但在这里我想它已经很好地满足了你的目的。
@Dmitri 此处未访问未初始化的变量
嗯。指针减法导致ptrdiff_tsizeof() 产生 size_t。 C确实定义哪个更宽或更高/相同的等级。所以商 ((char *)(&a+1)-(char *)a)/(sizeof(a[0])) 的类型不一定是 size_t,因此用 z 打印会导致 UB。只需使用 printf("The size of array a is %zu\n", sizeof a/sizeof a[0]); 就足够了。
(char *)(&a+1)-(char *)a 不是常数,可以在运行时计算,即使是固定大小的 a[10]。在这种情况下,sizeof(a)/sizeof(a[0]) 在编译时是常量。
J
Jency

最简单的答案:

#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;
}

M
Mano S

对于预定义的数组:

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

计算数组中的元素数:

 element _count =sizeof(a) / sizeof(a[0]);

P
Punisher

要知道在代码中显式声明并由其变量引用的固定数组的大小,您可以使用 sizeof,例如: int a[10] int len = sizeof(a)/sizeof(int) 但这通常没用,因为您已经知道答案。

但是如果你有一个指针,你不能使用 sizeof,这是一个原则问题。但是......由于数组作为用户的线性内存呈现,如果您知道最后一个元素地址并且如果您知道类型的大小,则可以计算大小,然后您可以计算它有多少个元素。例如:

#include <stdio.h>

int main(){
    int a[10];
    printf("%d\n", sizeof(a)/sizeof(int));
    int *first=a;
    int *last=&(a[9]);
    printf("%d\n", (last-first)+1);
}

输出:

10
10

此外,如果您不能利用编译时间,您可以:

#include <stdio.h>

int main(){
    int a[10];
    printf("%d\n", sizeof(a)/sizeof(int));
    void *first=a;
    void *last=&(a[9]);
    printf("%d\n", (last-first)/sizeof(int)+1);
}

P
Pygirl

注意:这个可以给你未定义的行为,正如 MM 在评论中指出的那样。

int a[10];
int size = (*(&a+1)-a) ;

有关详细信息,请参阅 herehere


这是技术上未定义的行为; * 运算符可能不适用于结束指针
“未定义的行为”是指 C 标准没有定义行为。如果您在程序中尝试它,那么任何事情都可能发生
@MM您是说 *(&a+1) - a; 与上面的 (&a)[1] - a; 不同,*(&a+1)(&a)[1] 不都算作 1 过去吗?
@QuentinUK 你的两个表达式都是一样的,x[y] 被定义为 *(x + (y))
@MM 我是这么认为的。但是 Arjun Sreedharan 的另一个答案有 38 个向上箭头,而这个是 -1。 Arjun Sreedharan 的回答没有提到未定义的行为。
R
RobertS supports Monica Cellio

除了已经提供的答案之外,我想通过使用来指出一个特殊情况

sizeof(a) / sizeof (a[0])

如果 acharunsigned charsigned char 的数组,则不需要使用 sizeof 两次,因为具有这些类型的一个操作数的 sizeof 表达式总是会导致 1

引自 C18,6.5.3.4/4:

“当 sizeof 应用于类型为 char、unsigned char 或 signed char(或其限定版本)的操作数时,结果为 1。”

因此,如果 acharunsigned charsigned char 类型的数组,则 sizeof(a) / sizeof (a[0]) 将等效于 NUMBER OF ARRAY ELEMENTS / 1。除以 1 是多余的。

在这种情况下,您可以简单地缩写并执行以下操作:

sizeof(a)

例如:

char a[10];
size_t length = sizeof(a);

如果您想要证明,这里是 GodBolt 的链接。

尽管如此,如果类型发生重大变化(尽管这种情况很少见),该部门仍会保持安全。


您可能更喜欢仍然使用除法的宏,因为将来类型可能会改变(尽管可能不太可能),并且在编译时知道除法,因此编译器会将其优化掉(如果它没有请更改你的编译器)。
@CacahueteFrito 是的,我同时也考虑过这一点。我把它作为答案的旁注。谢谢你。
我建议永远不要那么偷偷摸摸。它甚至没有增加任何性能改进,因为除法是在编译时完成的。
我仍然建议添加 / sizeof (a[0])。最佳编程实践通常旨在使代码健壮,即使部分代码被修改。如果有人更改了您的数组的内容,使其不包含 charunsigned charsigned char,您可能会发现自己面临一个难以发现的错误。