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

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

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

3035889 次浏览

内容提要:

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),但如果a改变你会有一个讨厌的bug如果你忘了改变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);
sizeof(array) / sizeof(array[0])

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

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

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

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

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

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

等等,广告无限。

“你引入了一种微妙的方式来射击自己的脚。

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

#define MY_ARRAY_LENGTH   15int myArray[MY_ARRAY_LENGTH];

如果你正在写C++,无论如何你都应该避免本机数组(除非你不能,在这种情况下,小心你的脚)。如果你正在编写C++,请使用STL的“向量”容器。“与数组相比,它们提供几乎相同的性能”,并且它们更有用!

// vector is a template, the <int> means it is a vector of intsvector<int> numbers;
// push_back() puts a new value at the end (or back) of the vectorfor (int i = 0; i < 10; i++)numbers.push_back(i);
// Determine the size of the arraycout << numbers.size();

请看:http://www.cplusplus.com/reference/stl/vector/

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: 28sizeof of parameter: 8Length of array: 7Length of parameter: 2

输出(在32位Windows操作系统中):

sizeof of array: 28sizeof of parameter: 4Length of array: 7Length of parameter: 1

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

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

这是示例输出

15492166721549216712---- diff----4The size of array a is 10

如果你真的想这样做来传递你的数组,我建议实现一个结构来存储一个指向你想要的数组类型的指针和一个表示数组大小的整数。然后你可以将它传递给你的函数。只需将数组变量值(指向第一个元素的指针)分配给该指针。然后你可以去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 aroundan array and size to your functions *//* This is a static implementation. You can get a dynamic implementation andcut 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);/*#endifEnd 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 andset the elements later. */MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};/* Now create a "MyTypeArray" which will use the array we justcreated internally. Really it will just store the value of the pointer"arr". Here we are manually setting the size. You can use the sizeoftrick 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;}
int size = (&arr)[1] - arr;

查看此链接的解释

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

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(数组名称)都是指针。

每个人都在使用的宏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最终将成为指针类型并使整个宏无效(就像你试图在带有指针参数的函数中使用宏一样)。

归根结底,在这个特别实例中,错误并不重要(所以我只是在浪费每个人的时间;哇!),因为你没有带有“数组”类型的表达式。但我认为关于预处理器评估的真正意义是微妙的。

C中数组的大小:

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

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

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

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

#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

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

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

代码最初在这里找到:

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

备注:这个可以给你未定义的行为作为评论中的由M. M.指出

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

有关更多详细信息,请参阅这里这里

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

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

最简单的答案:

#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 onlyint size;
size = sizeof(a)/sizeof(a[0]); // Method
printf("size = %d", size);return 0;}

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


太长别读:

#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)(定义如下)需要作为#1有问题(截至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;}

如果这些函数没有使用数组,而是将它们作为参数,那么以前的代码将无法编译,因此不可能有bug(考虑到使用了最近的编译器版本,或者使用了其他技巧),我们需要用值替换宏调用:

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):#0有问题

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

所以我们有#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))并希望它安全。我们需要C2Xstatic_assert()和一些GCC扩展:表达式中的语句和声明__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#1中提供了宏__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))

有些系统在#1中提供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(),但它需要一条消息)。

更优雅的解决方案将是

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

除了已经提供的答案之外,我想通过使用

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

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

引用C18,6.5.3.4/4:

"当将#0应用于类型为#1、#2或#3的操作数(或其限定版本)时,结果为#4。

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

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

sizeof(a)

例如:

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

如果你想要一个证明,这里有一个链接到GodBolt


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

对于预定义数组:

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

计算数组中的元素数:

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

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

输出:

1010

此外,如果您无法利用编译时间,您可以:

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