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

Mar*_*son 907 c memory arrays

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

也就是说,阵列可以容纳的元素数量是多少?

Mar*_*son 1143

执行摘要:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
Run Code Online (Sandbox Code Playgroud)

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

int a[17];
size_t n = sizeof(a);
Run Code Online (Sandbox Code Playgroud)

在我的计算机上,整数是4个字节长,所以n是68.

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

int a[17];
size_t n = sizeof(a) / sizeof(int);
Run Code Online (Sandbox Code Playgroud)

并获得正确的答案(68/4 = 17),但如果a您改变了类型, 如果您忘记更改它也会有一个讨厌的错误sizeof(int).

因此,首选除数是sizeof(a[0])数组的零齿数的大小.

int a[17];
size_t n = sizeof(a) / sizeof(a[0]);
Run Code Online (Sandbox Code Playgroud)

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

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

int a[17];
size_t n = NELEMS(a);
Run Code Online (Sandbox Code Playgroud)

  • **重要**:不要在这里停止阅读,阅读下一个答案!这仅适用于_stack_上的数组,例如,如果您使用malloc()或访问函数参数,则运气不佳.见下文. (423认同)
  • @Markus它适用于任何具有数组类型的变量; 这不一定是"在堆栈上".例如`static int a [20];`.但是你的评论对于那些可能没有意识到数组和指针之间差异的读者是有用的. (14认同)
  • 所有编译器应该是这种情况,因为sizeof的结果被定义为编译时常量. (9认同)
  • 生成的代码将是相同的,因为编译器在编译时知道*int_arr的类型(因此sizeof(*int_arr)的值).它将是一个常量,编译器可以相应地进行优化. (5认同)
  • 对于C或C++中的Windows API编程,在`WinNT.h`中定义了`ARRAYSIZE` makro(由其他头文件引入).因此WinAPI用户不需要定义自己的makro. (5认同)
  • 在许多 unix 系统上,'sys/param.h' 类似地定义了 'nitems(x)'。 (2认同)
  • 我们应该删除这个作为正确的答案.阅读@Markus评论.我浪费了几个小时,因为这个答案在malloc案例中不起作用. (2认同)
  • 使用 `size_t n = sizeof(a) / sizeof(a[0]);` 比 `int n = sizeof(a) / sizeof(a[0]);` 更好,因为 `size_t` 是 `size_t` 的返回类型大小()`。 (2认同)
  • 如果 `a` 是 `char`、`unsigned char` 或 `signed char` 的数组,则不需要使用 `sizeof(a) / sizeof(a[0])` - 引用自 C18,6.5.3.4 /4: *“当 sizeof 应用于类型为 char、unsigned char 或signed char(或其限定版本)的操作数时,结果为 1。”* 在这种情况下,您可以简单地执行 `sizeof(a) `,正如我的专用[答案](/sf/answers/4239556121/)中所指出的。 (2认同)

Eli*_*deb 752

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]) ));
}
Run Code Online (Sandbox Code Playgroud)

输出(在64位Linux操作系统中):

sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
Run Code Online (Sandbox Code Playgroud)

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

sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
Run Code Online (Sandbox Code Playgroud)

  • @Bbvarghe那是因为64位系统中的指针是8个字节(sizeof(intArray)),但是int仍然(通常)是4个字节长(sizeof(intArray [0])). (16认同)
  • @Pacerier:没有正确的代码 - 通常的解决方案是将长度与数组一起作为单独的参数传递. (13认同)
  • 如果仅传递指向第一个数组元素的指针,为什么是`参数的长度:2`? (10认同)
  • 等等,所以没有办法直接从指针访问数组并看到它的大小?这里是C新手. (10认同)
  • @Michael Trouw:如果让你感觉更好,你可以使用运算符语法:`(sizeof array/sizeof*array)`. (6认同)
  • @Nickolai这里的主要困惑是`sizeof`不是函数,而是C/C++中的运算符.如果从创建数组的同一范围调用,`sizeof`知道它的大小.但是,将数组作为参数传递时,该大小信息将丢失.该函数仅接收对内存中数组位置的引用,因此它被视为指针,用于所有目的.所以`sizeof`,当从函数内部调用时,只看到一个指针,并返回指针的大小.这些规则不适用于`sizeof`,因为它实际上不是一个函数,而且保留了范围. (3认同)
  • @Elideb谢谢你.我发布评论后,我想我在其他地方看过类似的内容.最终帮助我理解的是,当在声明数组的同一范围内调用sizeof时,它将其参数视为int [7]类型的参数(使用答案中的示例),但在另一个范围内调用时它将其参数视为int*类型的参数.基本上你说的一样,只是用我自己的话:) (3认同)
  • @MichaelTrouw,但正如在您自己的示例中chqrlie所演示的那样,它们也不是C中必需的。但是,是的,我离题,将C与您的HLL进行比较很愚蠢,它们的目标和意图截然不同。同样,不幸的是,如果您使用任何语言进行专业开发,都将使您的寿命缩短1-10年,因此您只能实现收支平衡:( (2认同)

Mag*_*off 127

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

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

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的回应.我想评论所有上述断言都被评估为TRUE. (3认同)
  • 我记得 CRAY 有一个带有 32 位 `char` 的 C。所有标准都说可以表示从 0 到 127 的整数值,其范围至少是 -127 到 127(char 有符号)或 0 到 255(char 无符号)。 (2认同)

unw*_*ind 47

"技巧"的大小是我所知道的最好的方式,其中一个很小但是(对我来说,这是一个主要的烦恼)括号使用的重要变化.

正如维基百科条目所表明的那样,C sizeof不是一个功能; 这是一个运营商.因此,除了参数是类型名称之外,它不需要围绕其参数使用括号.这很容易记住,因为它使参数看起来像一个转换表达式,它也使用括号.

所以:如果你有以下内容:

int myArray[10];
Run Code Online (Sandbox Code Playgroud)

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

size_t n = sizeof myArray / sizeof *myArray;
Run Code Online (Sandbox Code Playgroud)

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

当然,这也是编译时间,因此无需担心影响程序性能的划分.所以尽可能使用此表单.

当你有一个而不是一个类型时,最好在实际对象上使用sizeof,因为那时你不需要担心发出错误并说明错误的类型.

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

void send(const void *object, size_t size);
Run Code Online (Sandbox Code Playgroud)

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

int foo = 4711;
send(&foo, sizeof (int));
Run Code Online (Sandbox Code Playgroud)

现在,通过指定foo两个地方的类型,你已经介绍了一种微妙的射击方式.如果一个更改但另一个没有,则代码中断.因此,总是这样做:

send(&foo, sizeof foo);
Run Code Online (Sandbox Code Playgroud)

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

  • `sizeof` 可能是一个运算符,但根据 Linus Torvalds 的说法,它应该被视为一个函数。我同意。在这里阅读他的理由:https://lkml.org/lkml/2012/7/11/103 (3认同)

Arj*_*ran 37

int size = (&arr)[1] - arr;
Run Code Online (Sandbox Code Playgroud)

请查看此链接以获取解释

  • 小挑剔:指针减法的结果是'ptrdiff_t`类型.(通常在64位系统上,这将是比`int`更大的类型).即使你在这段代码中将`int`改为`ptrdiff_t`,如果`arr`占用超过一半的地址空间,它仍然有一个错误. (6认同)
  • @MM 另一个小问题:根据您的系统架构,地址空间几乎没有大多数系统上的指针大小那么大。例如,Windows 将 64 位应用程序的地址空间限制为 8TB 或 44 位。因此,即使您的数组大于地址空间 4.1TB 的一半,例如,它也不会是错误。只有在这些系统上您的地址空间超过 63 位时,才有可能遇到此类错误。一般来说,不要担心。 (2认同)
  • 这在技术上是未定义的行为,因为标准明确不允许取消引用超过数组的末尾(即使您不尝试读取存储的值) (2认同)

Cac*_*ito 28

我建议永远不要使用sizeof(即使可以使用)来获取数组的两种不同大小中的任何一种,无论是元素数量还是字节,这是我在这里展示的最后两种情况。对于这两种尺寸中的每一种,都可以使用下面显示的宏来使其更安全。究其原因是为了使代码维护者的明显意图,差异sizeof(ptr)sizeof(arr)第一眼(其中这样写的不是很明显),这样的错误是再为大家读码明显。


特尔;博士:

#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))
Run Code Online (Sandbox Code Playgroud)

must_be_array(arr)(定义如下)和-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)  __VA_OPT__(,)  __VA_ARGS__);\
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)
#define must_be_array(arr)  must_be(is_array(arr), "Not a `[]` !")
Run Code Online (Sandbox Code Playgroud)

有关于这个主题的重要错误: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);
}
Run Code Online (Sandbox Code Playgroud)

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


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


数组中元素的数量

这是最常见的,许多答案为您提供了典型的宏ARRAY_SIZE

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))
Run Code Online (Sandbox Code Playgroud)

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

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))
Run Code Online (Sandbox Code Playgroud)

具有多个PTRDIFF_MAX成员的数组将为这个宏的签名版本提供无效值,但是从阅读 C17::6.5.6.9 来看,这样的数组已经在玩火了。只有ARRAY_SIZE并且size_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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Run Code Online (Sandbox Code Playgroud)

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

#define ARRAY_SBYTES(arr)   ((ssize_t)ARRAY_BYTES(arr))
Run Code Online (Sandbox Code Playgroud)

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

用法示例:

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

        memset(arr, 0, ARRAY_BYTES(arr));
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

        memset(arr, 0, sizeof(arr[0]) * nmemb);
}
Run Code Online (Sandbox Code Playgroud)

更新 (23/apr/2020):-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]))并希望使其安全。我们将需要 C11_Static_assert()和一些 GCC 扩展:表达式中的语句和声明__builtin_types_compatible_p

#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), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        sizeof(arr) / sizeof((arr)[0]);                                 \
}                                                                       \
)
Run Code Online (Sandbox Code Playgroud)

NowARRAY_SIZE()是完全安全的,因此它的所有衍生物都是安全的。


更新:libbsd 提供__arraycount()

Libbsd提供了__arraycount()in宏<sys/cdefs.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), "Not a `[]` !")

#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))
Run Code Online (Sandbox Code Playgroud)

有些系统提供nitems()in<sys/param.h>代替,有些系统同时提供。您应该检查您的系统,并使用您拥有的系统,并且可能使用一些预处理器条件来实现可移植性并支持两者。


更新:允许在文件范围内使用宏:

不幸的是,({})gcc 扩展名不能在文件范围内使用。为了能够在文件范围内使用宏,静态断言必须在sizeof(struct {}). 然后,乘以0不影响结果。转换 to(int)可能很好地模拟一个返回的函数(int)0(在这种情况下它不是必需的,但它可以重用于其他事情)。

另外, 的定义ARRAY_BYTES()可以简化一些。

#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)  __VA_OPT__(,)  __VA_ARGS__);\
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)
#define must_be_array(arr)      must_be(is_array(arr), "Not a `[]` !")

#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))
Run Code Online (Sandbox Code Playgroud)

笔记:

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

我还使用了以下 C11 功能。但是,可以使用一些肮脏的技巧来克服使用旧标准的缺失(参见例如:C 代码中的“:-!!”是什么?)。

我也使用以下扩展名,但有一种标准的 C 方法可以做同样的事情。

  • 您介意解释一下为什么投反对票吗?这展示了一个不安全且常见的构造(`sizeof(arr)`)的解决方案,该结构在其他地方没有显示:`ARRAY_BYTES(arr)`。 (4认同)
  • ...,所以我坚持主要结论:单个 `sizeof` 显然是不安全的(原因在答案中),并且每次不使用宏而是使用我提供的结构更加不安全,所以唯一的可行的方法是宏。 (4认同)
  • 我想您可能对数组和指针之间的区别感到困惑。这是 C 语言中的一个基本概念,作为学习 C 语言的一部分,程序员应该确保他们理解这种差异。试图假装 C 语言是另一种语言只会导致不愉快。 (4认同)
  • ARRAY_SIZE 很常见,可以自由使用,而 ARRAY_BYTES 的名称非常明确,应该定义在 ARRAY_SIZE 旁边,以便用户可以轻松地看到两者,并且通过其用法,我认为阅读代码的任何人都不会怀疑什么确实如此。我的意思是不要使用简单的“sizeof”,而是使用这种结构;如果你每次都想写这些结构,你可能会犯错误(如果你复制粘贴很常见,如果你每次都写它们也很常见,因为它们有很多括号)... (3认同)
  • @MarkHarrison 我确实知道指针和数组之间的区别。但有时候我有一个函数,后来我将其重构为小函数,首先是一个数组,后来是一个指针,这就是如果你忘记更改 sizeof,你就会搞砸它,并且很容易看不到其中一个。 (3认同)
  • @hyde 另外,我知道差异并不意味着每个人都知道差异,为什么不使用基本上消除 100% 这些错误的东西呢?这个 bug 几乎进入了 Linux;正如他所说,它到达了 Linus,这意味着它通过了很多审查,也意味着同样的 bug 可能已经进入 Linux 内核的其他部分。 (3认同)
  • 别听这个人胡言乱语。如果你用这些宏填充我的代码库,我会解雇你。 (2认同)

Moh*_*bli 25

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

len = sizeof(arr)/sizeof(arr[0])
Run Code Online (Sandbox Code Playgroud)

代码最初在这里找到: C程序查找数组中的元素数


Abh*_*tri 20

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

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

int noofele = sizeof(arr)/sizeof(int);
Run Code Online (Sandbox Code Playgroud)

或者,如果您不知道数组的数据类型,可以使用以下内容:

noofele = sizeof(arr)/sizeof(arr[0]);
Run Code Online (Sandbox Code Playgroud)

注意:只有在运行时未定义数组(如malloc)且数组未在函数中传递时,此方法才有效.在这两种情况下,arr(数组名称)都是指针.

  • “ int noofele = sizeof(arr)/ sizeof(int);”仅比编码“ int noofele = 9;”好一半。如果数组大小改变,使用`sizeof(arr)`可以保持灵活性。然而,如果更改arr []的类型,则需要更新sizeof(int)。最好使用`sizeof(arr)/ sizeof(arr [0])`,即使类型众所周知。不清楚为什么将`int`用作`noofele`而不是`size_t`,即由sizeof()返回的类型。 (2认同)

小智 17

ARRAYELEMENTCOUNT(x)每个人都在使用的宏评估错误.实际上,这只是一个敏感问题,因为您不能拥有导致"数组"类型的表达式.

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

ARRAYELEMENTCOUNT(p + 1);
Run Code Online (Sandbox Code Playgroud)

实际上评估为:

(sizeof (p + 1) / sizeof (p + 1[0]));
Run Code Online (Sandbox Code Playgroud)

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

ARRAYELEMENTCOUNT(p + 1);
Run Code Online (Sandbox Code Playgroud)

它正确评估为:

(sizeof (p + 1) / sizeof (p + 1)[0]);
Run Code Online (Sandbox Code Playgroud)

这显然与数组的大小没有太大关系.我刚刚注意到很多错误都没有真正观察C预处理器的工作原理.您总是包装宏参数,而不是可能涉及的表达式.


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

在一天结束时,在这个特定的例子中,错误并不重要(所以我只是浪费每个人的时间; huzzah!),因为你没有带有'array'类型的表达式.但我认为关于预处理器评估小数的真正意义重大.

  • 有没有理由不使用`(sizeof(x)/ sizeof(*x))` (3认同)
  • 感谢您的解释。原始版本会导致编译时错误。Clang 报告“下标值不是数组、指针或向量”。在这种情况下,这似乎是更可取的行为,尽管您对宏中的评估顺序的评论很好。 (2认同)

And*_*ler 14

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

#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",        " " },
};
Run Code Online (Sandbox Code Playgroud)

但是这些常量也可以在编译时使用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);
Run Code Online (Sandbox Code Playgroud)

请注意,此代码适用于C和C++.对于具有两个以上维度的数组使用

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
Run Code Online (Sandbox Code Playgroud)

无限的,无限的.


Yog*_*H T 14

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
Run Code Online (Sandbox Code Playgroud)

  • 奇怪的是,代码使用的是“ size_t size_of_element”而使用了“ int”,而“ int n = sizeof(a)/ sizeof(a [0]);” `,而不是`size_t n = sizeof(a)/ sizeof(a [0]);` (2认同)
  • @YogeeshHT 对于像 `char a[INT_MAX + 1u];` 这样的非常大的数组,`int n = sizeof (a) / sizeof (a[0]); 中使用的 `int n` 是不够的(它是 UB) . 使用 `size_t n = sizeof (a) / sizeof (a[0]);` 不会导致这个问题。 (2认同)

Ted*_*val 12

sizeof(array) / sizeof(array[0])
Run Code Online (Sandbox Code Playgroud)


Oha*_*had 9

"你已经介绍了一种在脚下射击自己的微妙方式"

C'原生'数组不存储它们的大小.因此,建议将数组的长度保存在单独的变量/ const中,并在每次传递数组时传递它,即:

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];
Run Code Online (Sandbox Code Playgroud)

你应该总是避免使用原生数组(除非你不能,在这种情况下,请注意你的脚).如果您正在编写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();
Run Code Online (Sandbox Code Playgroud)

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

  • 问题是关于C,而不是C++。所以没有STL。 (21认同)
  • 这个答案正在[元上讨论](https://meta.stackoverflow.com/questions/420337)。 (15认同)
  • 这个答案明显偏离主题,应该删除。 (12认同)
  • `std::array&lt;int,10&gt;` 是普通数组的直接替代品。仅当您确实希望其大小成为运行时变量时才使用“std::vector”。`std::array` 不会将大小存储在内存中的任何位置,但它将大小作为类型的一部分关联起来,为您提供所有效率且没有维护危险。 (4认同)
  • 问题所问的语言是 C,有什么替代“本机数组”的方法?我不知道有什么——有静态数组、本地数组和动态分配数组,但它们都是“原生”AFAICT。 (2认同)

And*_*ent 9

#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这仅适用于实际数组,而不适用于指向数组的指针. (6认同)

小智 6

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

typedef struct {
     int *array;
     int elements;
} list_s;
Run Code Online (Sandbox Code Playgroud)

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

  • “int elements”与“size_t elements”有什么理由吗? (7认同)

Kei*_*van 6

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

int array[10];
size_t sizeOfArray = sizeof(array)/sizeof(int);
Run Code Online (Sandbox Code Playgroud)


Joe*_*ici 5

如果你真的想这样做来传递你的数组,我建议实现一个结构来存储一个指向你想要数组的类型的指针和一个表示数组大小的整数。然后你可以将它传递给你的函数。只需将数组变量值(指向第一个元素的指针)分配给该指针。然后你可以去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;
}
Run Code Online (Sandbox Code Playgroud)

  • 无法对未处理溢出的“strcpy(d.name, name);”代码进行投票。 (4认同)
  • 6 年后,代码仍然包含对“strcpy”的不安全调用。不要使用此代码。 (2认同)