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

How do I determine the size of my array in C?

提问人:Mark Harrison 提问时间:9/1/2008 最后编辑:Daniel WalkerMark Harrison 更新时间:9/2/2023 访问量:3212892

问:

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

也就是说,数组可以容纳多少元素?

数组 C

评论

6赞 T S 8/25/2017
有关类型安全,请参阅 stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers
0赞 Peter Mortensen 9/15/2022
答案是元问题的主题。
1赞 puppydrum64 12/14/2022
如果答案没有意义,请记住,当 C 编译为汇编时,数组的大小信息会丢失。当您声明类似的东西时,5 不会出现在您编译的可执行文件中。int foo[5];
2赞 Supergamer 1/9/2023
sizeof(array)/sizeof(array[0])

答:

1693赞 Mark Harrison 9/1/2008 #1

摘要:

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),但是如果更改了类型,如果您忘记更改,您将遇到一个令人讨厌的错误 以及。asizeof(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);

评论

7赞 Mark Harrison 9/22/2013
生成的代码将是相同的,因为编译器在编译时知道 *int_arr 的类型(因此知道 sizeof(*int_arr) 的值)。它将是一个常量,编译器可以相应地进行优化。
11赞 Mark Harrison 9/22/2013
所有编译器都应该如此,因为 sizeof 的结果被定义为编译时常量。
654赞 Markus 1/27/2014
重要提示:不要在这里停止阅读,请阅读下一个答案!这仅适用于堆栈上的数组,例如,如果您使用 malloc() 或访问函数参数,则不走运。见下文。
8赞 Lumi 4/23/2014
对于使用 C 或 C++ 进行的 Windows API 编程,有 中定义的 makro(由其他标头拉入)。因此,WinAPI 用户不需要定义自己的 makro。ARRAYSIZEWinNT.h
34赞 M.M 10/6/2014
@Markus它适用于任何具有数组类型的变量;这不一定是“在堆栈上”。例如 .但是您的注释对于可能没有意识到数组和指针之间区别的读者很有用。static int a[20];
16赞 T Percival 9/1/2008 #2
sizeof(array) / sizeof(array[0])

评论

0赞 RobertS supports Monica Cellio 3/6/2020
根据类型 has 的不同,如果是 或 的数组,则不需要使用 - 引自 C18,6.5.3.4/4:“当 sizeof 应用于类型为 char、unsigned char 或 signed char(或其限定版本)的操作数时,结果为 1。在这种情况下,您可以简单地按照我的专用答案中的说明进行操作。arraysizeof(array) / sizeof(array[0])arraycharunsigned charsigned charsizeof(array)
180赞 Magnus Hoff 9/1/2008 #3

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

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

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

评论

2赞 vonbrand 2/2/2013
我记得 CRAY 有 32 位的 C。所有标准都说可以表示从 0 到 127 的整数值,其范围至少为 -127 到 127(char 有符号)或 0 到 255(char 无符号)。char
1赞 Skizz 9/1/2008
@ Magnus:该标准将 sizeof 定义为产生对象中的字节数,并且 sizeof (char) 始终为 1。字节中的位数是特定于实现的。编辑:ANSI C++ 标准第 5.3.3 节 Sizeof:“sizeof 运算符在其操作数的对象表示中产生字节数。[...]sizeof (char)、sizeof (signed char) 和 sizeof (unsigned char) 为 1;应用于任何其他基本类型的 sizeof 的结果是实现定义的。
1赞 Skizz 9/1/2008
第 1.6 节 C++ 内存模型:“C++ 内存模型中的基本存储单元是字节。一个字节至少足够大,可以包含基本执行字符集的任何成员,并且由连续的位序列组成,其数量由实现定义。
63赞 unwind 10/15/2008 #4

“诀窍”是我所知道的最好的方法,括号的使用有一个小但(对我来说,这是一个主要的烦恼)重要的变化。sizeof

正如维基百科条目所明确指出的,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);

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

评论

0赞 Pacerier 9/21/2013
顺便说一句,它们在处理器级别是相同的指令吗?是否需要比 ?sizeof(int)sizeof(foo)
0赞 quetzalcoatl 10/5/2013
@Pacerier:不,它们是相同的。想想 vs.在这里,括号纯粹是纯粹的美学。int x = 1+1;int x = (1+1);
0赞 unwind 1/13/2016
@Aidiakapi 事实并非如此,请考虑 C99 VLA。
5赞 SO Stinks 3/17/2020
sizeof可能是一个运算符,但根据 Linus Torvalds 的说法,它应该被视为一个函数。我同意。在这里阅读他的理性:lkml.org/lkml/2012/7/11/103
1赞 Eric Duminil 11/28/2020
为什么省略 parens 会使其更具可读性? 例如,可能意味着。我知道这没有意义,但恕我直言,最好还是明确。sizeof myArray / sizeof *myArray;sizeof(myArray / sizeof *myArray);
19赞 Andreas Spindler 8/17/2011 #5

对于多维数组,它有点复杂。通常,人们会定义显式的宏常数,即

#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])

等等,无穷无尽。

0赞 Ohad 11/15/2011 #6

“你引入了一种微妙的搬起石头砸自己的脚的方式”

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

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

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

// 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/

评论

21赞 alx - recommends codidact 4/23/2020
问题是关于C,而不是C++。所以没有 STL。
12赞 Lundin 9/12/2022
这个答案显然是跑题了,应该删除。
15赞 cigien 9/12/2022
这个答案正在 meta 上讨论
4赞 Peter Cordes 9/13/2022
std::array<int,10>是普通阵列的直接替代品。仅当您实际希望其大小为运行时变量时才使用。 不会将大小存储在内存中的任何位置,但它会将大小关联为类型的一部分,从而为您提供所有效率,而不会带来维护风险。std::vectorstd::array
2赞 Jonathan Leffler 9/14/2022
在C语言中,除了“原生数组”之外,还有什么替代方案,这是问题所问的语言?我不知道——有静态数组、局部数组和动态分配数组,但它们都是“原生”AFAICT。
1087赞 Elideb 4/27/2012 #7

如果您处理未作为参数接收的数组,则该方法是正确的方法。作为参数发送到函数的数组被视为指针,因此将返回指针的大小,而不是数组的大小。sizeofsizeof

因此,在函数内部,此方法不起作用。相反,请始终传递一个附加参数,指示数组中的元素数。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

评论

13赞 Bbvarghe 8/5/2013
为什么只传递指向第一个数组元素的指针?length of parameter:2
25赞 Elideb 8/29/2013
@Bbvarghe 这是因为 64 位系统中的指针是 8 个字节 (sizeof(intArray)),但 int 仍然(通常)是 4 个字节长 (sizeof(intArray[0]))。
23赞 Jean Hominal 10/5/2013
@Pacerier:没有正确的代码 - 通常的解决方案是将长度与数组一起作为单独的参数传递。
15赞 sudo 12/1/2013
等等,所以没有办法直接从指针访问数组并查看其大小?这里是 C 的新手。
7赞 chqrlie 4/29/2015
@Michael Trouw:你可以使用运算符语法 if is makes you feel better: .(sizeof array / sizeof *array)
3赞 Shih-En Chou 5/3/2013 #8

您可以使用运算符。源代码如下:&

#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

评论

9赞 Dmitri 9/12/2014
我没有投反对票,但这就像用砖头钉子,因为你没有注意到你旁边有一把锤子。此外,人们往往不愿意使用未初始化的变量......但在这里,我想它很好地满足了你的目的。
2赞 M.M 10/6/2014
@Dmitri此处未访问未初始化的变量
1赞 chux - Reinstate Monica 5/21/2016
指针减法导致 . 结果为 。C 没有定义哪个更宽或更高/相同等级。因此,商的类型不确定,因此打印会导致UB。只需使用就足够了。ptrdiff_tsizeof()size_t((char *)(&a+1)-(char *)a)/(sizeof(a[0]))size_tzprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
1赞 chux - Reinstate Monica 5/22/2018
(char *)(&a+1)-(char *)a不是一个常量,可以在运行时计算,即使使用固定大小的 . 在本例中,是编译时完成的常量。a[10]sizeof(a)/sizeof(a[0])
6赞 Joel Dentici 6/13/2013 #9

如果你真的想这样做来传递你的数组,我建议实现一个结构来存储一个指向你想要的类型的指针,一个数组和一个表示数组大小的整数。然后,你可以把它传递给你的函数。只需将数组变量值(指向第一个元素的指针)分配给该指针即可。然后你可以去获取第 i 个元素并用于获取数组中的元素数量。Array.arr[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;
}

评论

4赞 chux - Reinstate Monica 5/21/2016
无法对未处理溢出的代码进行投票。strcpy(d.name, name);
2赞 Roland Illig 9/11/2022
6 年后,代码仍然包含对 的不安全调用。请勿使用此代码。strcpy
42赞 Arjun Sreedharan 12/8/2013 #10
int size = (&arr)[1] - arr;

查看此链接以获取说明

评论

9赞 M.M 10/6/2014
小吹毛求疵:指针减法的结果有类型。(通常在 64 位系统上,这将是比 更大的类型。即使您在此代码中更改为,如果占用了一半以上的地址空间,它仍然有一个错误。ptrdiff_tintintptrdiff_tarr
2赞 Aidiakapi 1/9/2016
@M.M 另一个小的吹毛求疵:根据您的系统体系结构,地址空间远不如大多数系统上的指针大小那么大。例如,Windows 将 64 位应用程序的地址空间限制为 8TB 或 44 位。因此,即使您的阵列大于地址空间的一半(例如4.1TB),也不会是一个错误。只有当您的地址空间在这些系统上超过 63 位时,才有可能遇到此类错误。一般来说,不用担心。
1赞 Ruslan 1/10/2018
@Aidiakapi 在 32 位 x86 Linux 或 Windows 上,您可以选择 3G/1G 用户/内核拆分,这允许您将数组大小设置为地址空间大小的 75%。/3G
1赞 chux - Reinstate Monica 5/22/2018
视为全局变量。 声明失败,因为不是常量。foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];buf3[](&buf1)[1] - buf1
4赞 M.M 2/16/2020
这在技术上是未定义的行为,因为该标准明确不允许在数组末尾之后取消引用(即使您不尝试读取存储的值)
25赞 Abhitesh khatri 1/19/2014 #11

如果您知道数组的数据类型,则可以使用如下方法:

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

评论

4赞 chux - Reinstate Monica 5/21/2016
int noofele = sizeof(arr)/sizeof(int);只比编码好一半。使用时,如果阵列大小发生变化,则保持灵活性。然而,如果类型发生变化,则需要更新。即使类型众所周知,也最好使用。不清楚为什么使用 for vs. ,返回的类型。int noofele = 9;sizeof(arr)sizeof(int)arr[]sizeof(arr)/sizeof(arr[0])intnoofelesize_tsizeof()
23赞 user2379628 2/28/2014 #12

每个人都在使用的宏计算不正确。实际上,这只是一个敏感问题,因为你不能有导致“数组”类型的表达式。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

归根结底,在这种特殊情况下,错误并不重要(所以我只是在浪费每个人的时间;呵呵!),因为你没有带有“数组”类型的表达式。但实际上,我认为关于预处理器评估微妙之处的一点很重要。

评论

2赞 Mark Harrison 2/28/2014
谢谢你的解释。原始版本会导致编译时错误。Clang 报告“下标值不是数组、指针或向量”。在这种情况下,这似乎是更可取的行为,尽管您对宏中计算顺序的注释得到了很好的采纳。
1赞 2/28/2014
我没有想到编译器投诉是错误类型的自动通知。谢谢!
3赞 seriousdev 3/28/2017
有理由不使用吗?(sizeof (x) / sizeof (*x))
18赞 Yogeesh H T 11/20/2015 #13

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

评论

4赞 chux - Reinstate Monica 5/21/2016
好奇代码还用过和不用size_t size_of_elementintint n = sizeof (a) / sizeof (a[0]); size_t n = sizeof (a) / sizeof (a[0]);
1赞 Pallav Raj 7/22/2017
嗨,@Yogeesh H T,你能回答chux的疑问吗?我也很好奇 int n=sizeof(a)/sizeof(a[0]) 是如何给出数组长度的,以及为什么我们不使用 size_t 作为数组的长度。谁能回答?
1赞 Yogeesh H T 7/24/2017
@Brain sizeof(a) 给出数组中存在的所有元素的 sizeof sizeof(a[0]) 给出第一个元素的 sizeof。假设 a = {1,2,3,4,5};sizeof(a) = 20bytes (if sizeof(int)= 4bytes 乘以 5), sizeof(a[0]) = 4bytes, so 20/4 = 5 即元素数
3赞 chux - Reinstate Monica 5/22/2018
@YogeeshHT 对于非常大的数组,如 ,如 中使用 是不够的(它是 UB)。使用不会产生此问题。char a[INT_MAX + 1u];int nint n = sizeof (a) / sizeof (a[0]);size_t n = sizeof (a) / sizeof (a[0]);
6赞 Paulo Pinheiro 2/17/2016 #14

最好的方法是将此信息保存在结构中,例如,在结构中:

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

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

评论

7赞 chux - Reinstate Monica 5/21/2016
vs. 有什么理由吗?int elementssize_t elements
12赞 Andy Nugent 9/12/2016 #15
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

评论

6赞 David Schwartz 1/8/2017
请注意,这仅适用于实际数组,而不适用于恰好指向数组的指针。
30赞 Mohd Shibli 5/27/2017 #16

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

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

代码最初在这里找到:

查找数组中元素数的 C 程序

评论

1赞 VLL 9/13/2022
这个问题已经有很多答案了。这个答案增加了什么,而接受的答案没有?
-4赞 Pygirl 7/18/2018 #17

注意:正如 M.M 在评论中指出的那样,这可能会给你未定义的行为。

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

有关更多详细信息,请参阅此处和此处

评论

2赞 M.M 8/28/2018
从技术上讲,这是未定义的行为;运算符不能应用于过去的末尾指针*
3赞 M.M 8/29/2018
“未定义的行为”是指 C 标准未定义行为。如果你在程序中尝试它,那么任何事情都可能发生
0赞 QuentinUK 2/16/2020
@M.M你是说和上面不一样,不要两者都算作1?*(&a+1) - a;(&a)[1] - a;*(&a+1)(&a)[1]
0赞 M.M 2/16/2020
@QuentinUK您的两个表达式都相同,则定义为x[y]*(x + (y))
0赞 QuentinUK 2/16/2020
@M.M我是这么认为的。但另一个答案,由 Arjun Sreedharan 提供,有 38 个向上箭头,这是 -1。Arjun Sreedharan的回答没有提到未定义的行为。
6赞 Keivan 8/20/2018 #18

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

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

评论

0赞 Roland Illig 9/11/2022
应该是 而不是 .size_t sizeOfArrayint
2赞 Jency 8/8/2019 #19

最简单的答案:

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

评论

1赞 Peter Mortensen 9/13/2022
解释是有序的。例如,它以什么方式最简单?想法/要点是什么?它与以前的答案有何不同?运行它的结果是什么?从帮助中心:“......始终解释为什么您提出的解决方案是合适的,以及它是如何工作的”。请通过编辑(更改)您的答案来回复,而不是在评论中(没有“编辑:”,“更新:”或类似内容 - 答案应该看起来像今天写的一样)。
43赞 alx - recommends codidact 8/17/2019 #20

我建议永远不要使用 sizeof(即使可以使用)来获取数组的两种不同大小中的任何一种,无论是元素数还是字节数,这是我在这里展示的最后两种情况。对于这两种大小中的每一种,下面显示的宏都可用于使其更安全。原因是为了让维护者清楚地看到代码的意图,并且乍一看 sizeof(ptr) 与 sizeof(arr) 的区别(以这种方式编写并不明显),因此对于阅读代码的每个人来说,错误都是显而易见的。


TL;博士:

#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)(定义见下文)需要 IS 因为 -Wsizeof-pointer-div 有问题(截至 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(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_SIZEsizeof(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);
}

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

今天我发现 GCC 中的新警告仅在宏在不是系统标头的标头中定义时才有效。如果您在系统中安装的标头(通常是或)()中定义宏,编译器将不会发出警告(我尝试了 GCC 9.3.0)。/usr/local/include//usr/include/#include <foo.h>

因此,我们拥有并希望确保它的安全。我们将需要 C2X 和一些 GCC 扩展:表达式中的语句和声明__builtin_types_compatible_p#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))static_assert()

#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))

笔记:

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

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

评论

3赞 alx - recommends codidact 8/17/2019
ARRAY_SIZE足够通用,可以自由使用,并且ARRAY_BYTES的名称非常明确,应该在ARRAY_SIZE旁边定义,以便用户可以轻松查看,并且通过它的用法,我认为任何阅读代码的人都不会怀疑它的作用。我的意思是不要使用简单的 ,而是使用这种结构来代替;如果你每次都想写这些结构,你很可能会犯一个错误(如果你复制粘贴,这很常见,如果你每次都写它们,也很常见,因为它们有很多括号)......sizeof
4赞 alx - recommends codidact 8/17/2019
...,所以我站在主要结论上:单曲显然是不安全的(原因在答案中),而不是使用宏而是使用我提供的结构,每次都更加不安全,所以唯一的办法是宏。sizeof
4赞 Mark Harrison 8/18/2019
我想你可能会对数组和指针之间的区别感到困惑。这是 C 语言中的一个基本概念,程序员应该确保他们在学习 C 语言时理解这种差异。
3赞 alx - recommends codidact 8/18/2019
@MarkHarrison我确实知道指针和数组之间的区别。但是有时候我有一个函数,后来我把它重构成小函数,首先是一个数组,后来是一个指针,如果你忘记改变 sizeof,你就会搞砸它,很容易看不到其中一个。
4赞 Byron 7/10/2020
别听这家伙的话。如果你用这些宏填充我的代码库,我会解雇你。
3赞 reflex0810 1/28/2020 #21

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

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

评论

1赞 Roland Illig 9/11/2022
上下文太少,无法避免此模式的所有陷阱。
-2赞 RobertS supports Monica Cellio 3/6/2020 #22

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

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

If 是 的数组,或者不需要使用两次,因为具有这些类型之一操作数的表达式总是会导致 。acharunsigned charsigned charsizeofsizeof1

引自 C18,6.5.3.4/4:

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

因此,将等价于 if 是 类型的数组,或 。通过 1 的划分是多余的。sizeof(a) / sizeof (a[0])NUMBER OF ARRAY ELEMENTS / 1acharunsigned charsigned char

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

sizeof(a)

例如:

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

如果你想要一个证据,这里是一个指向 GodBolt 的链接。


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

评论

2赞 alx - recommends codidact 4/23/2020
您可能更喜欢仍然应用带有除法的宏,因为类型将来可能会发生变化(尽管可能不太可能),并且除法在编译时是已知的,因此编译器将对其进行优化(如果没有,请更改您的编译器)。
1赞 RobertS supports Monica Cellio 4/23/2020
@CacahueteFrito 是的,我在此期间也考虑过这个问题。我把它作为答案的旁注。谢谢。
2赞 alx - recommends codidact 7/20/2020
我建议永远不要那么鬼鬼祟祟。它甚至没有增加任何性能改进,因为划分是在编译时完成的。
4赞 Tyrel Kostyk 4/17/2021
我仍然建议添加.最佳编程实践通常旨在使代码健壮,即使部分代码被修改。如果有人更改了数组的内容,使其不包含 、 或 ,您可能会发现自己面临一个难以找到的错误。/ sizeof (a[0])charunsigned charsigned char
-5赞 Mano S 7/3/2021 #23

对于预定义数组:

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

计算数组中的元素数:

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

评论

2赞 VLL 9/13/2022
这个问题已经有很多答案了。这个答案增加了什么,而接受的答案没有?
-3赞 Punisher 4/26/2022 #24

若要了解在代码中显式声明并由其变量引用的固定数组的大小,可以使用 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);
}

评论

0赞 Roland Illig 9/11/2022
虽然这个答案试图解释数组被转换为指针的陷阱,但它并没有足够清楚地说明它。应该有错误行为的示例代码。
0赞 VLL 9/13/2022
这个问题已经有很多答案了。这个答案增加了现有答案所没有的什么?
0赞 Davis Herring 9/13/2022
我不确定它是否算作“确定数组的大小”,如果你从指向(或过去)最后一个元素的指针开始。(你也不能随便减去 s。void*
0赞 Punisher 9/14/2022
这个答案对于高级C程序员来说应该很清楚,以前没有人说过这一点。
0赞 James Z.M. Gao 9/2/2023 #25
#ifndef __cplusplus
   /* C version */
#  define ARRAY_LEN_UNSAFE(X) (sizeof(X)/sizeof(*(X)))
#  define ARRAY_LEN(X) (ARRAY_LEN_UNSAFE(X) + 0 * sizeof((typeof(*(X))(*[1])[ARRAY_LEN_UNSAFE(X)]){0} - (typeof(X)**)0))
#else
   /* C++ version */
   template <unsigned int N> class __array_len_aux    { public: template <typename T, unsigned int M> static const char (&match_only_array(T(&)[M]))[M]; };
   template <>               class __array_len_aux<0> { public: template <typename T>                 static const char (&match_only_array(T(&)))[0]; };
#  define ARRAY_LEN(X) sizeof(__array_len_aux<sizeof(X)>::match_only_array(X))
#endif


/* below are verifying codes */
#include <assert.h>

void * a0[0];
void * a1[9];
void * aa0[0];
void * aa1[5][10];
void *p;
struct tt {
    char x[10];
    char *p;
} t;

static_assert(ARRAY_LEN(a0) == 0, "verify [0]");
static_assert(ARRAY_LEN(aa0) == 0, "verify [0][N]");
static_assert(ARRAY_LEN(a1) == 9, "verify [N]");
static_assert(ARRAY_LEN(aa1) == 5, "verify [N][M]");
static_assert(ARRAY_LEN(aa1[0]) == 10, "verify inner array of [N][M]");
static_assert(ARRAY_LEN(t.x) == 10, "verify array in struct");
//static_assert(ARRAY_LEN(p) == 0, "should parse error");
//static_assert(ARRAY_LEN(t.p) == 0, "should parse error");

以下版本仅依赖于 。和版本具有相同的行为,只接受数组(包括 0 长度或多维数组),但拒绝任何指针类型。CARRAY_LENtypeof()C++C