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


Answers:


1258

执行摘要:

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

6
生成的代码将是相同的,因为编译器在编译时就知道* int_arr的类型(因此知道sizeof(* int_arr)的值)。这将是一个常数,编译器可以相应地进行优化。
马克·哈里森

10
所有编译器都应该如此,因为sizeof的结果被定义为编译时常量。
马克·哈里森

449
重要提示:不要在这里停止阅读,请阅读下一个答案!这仅适用于堆栈上的数组,例如,如果您正在使用malloc()或访问函数参数,那么您很不走运。见下文。
Markus 2014年

7
对于使用C或C ++进行Windows API编程,有一个在中ARRAYSIZE定义的makro WinNT.h(被其他标头拉入)。因此,WinAPI用户无需定义自己的Makro。
Lumi 2014年

17
@Markus它适用于任何具有数组类型的变量;这不必是“堆栈上的”。例如static int a[20];。但是您的评论对可能没有意识到数组和指针之间的区别的读者很有用。
2014年

807

如果您要处理未作为参数接收到的数组,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

10
为什么length of parameter:2只传递指向第一个数组元素的指针?
Bbvarghe

16
@Bbvarghe这是因为64位系统中的指针为8个字节(sizeof(intArray)),但ints仍然(通常)仍为4个字节长(sizeof(intArray [0]))。
Elideb

13
@Pacerier:没有正确的代码-通常的解决方案是将长度和数组作为单独的参数传递。
Jean Hominal

10
等待,所以无法直接从指针访问数组并查看其大小?这里是C的新手。
sudo 2013年

7
@Michael Trouw:如果可以使您感觉更好,则可以使用运算符语法:(sizeof array / sizeof *array)
chqrlie 2015年

134

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

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

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

1
@ Magnus:该标准将sizeof定义为产生对象中的字节数,并且sizeof(char)始终为1。字节中的位数是特定于实现的。编辑:ANSI C ++标准第5.3.3节。sizeof:“ sizeof运算符产生其操作数的对象表示形式中的字节数。sizeof(char),sizeof(signed char)和sizeof(unsigned char)是1;将sizeof应用于任何其他基本类型的结果是实现定义的。”
Skizz

第1.6节C ++内存模型:“ C ++内存模型中的基本存储单元是字节。一个字节至少大到足以包含基本执行字符集的任何成员,并且由连续的位序列(即数字)组成其中是实施定义的。”
Skizz

2
我记得CRAY的C具有char32位。所有标准说的是,可以表示从0到127的整数值,并且其范围至少为-127到127(带符号的字符)或0到255(无符号的字符)。
vonbrand

5
这是一个很好的回应。我想评论所有以上断言都被评估为TRUE。
Javad

49

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

@Pacerier:不,它们是相同的。想想int x = 1+1;int x = (1+1);。在这里,括号纯粹是完全美学的。
quetzalcoatl

@Aidiakapi事实并非如此,请考虑使用C99 VLA。
放松

@unwind谢谢,我已经纠正了。要更正我的意见,sizeof在C ++和C89中将始终保持不变。使用C99的可变长度数组,可以在运行时对其进行评估。
Aidiakapi

2
sizeof可能是运算符,但根据Linus Torvalds,应将其视为函数。我同意。在这里阅读他的理论:lkml.org/lkml/2012/7/11/103
Person Person II博士

37
int size = (&arr)[1] - arr;

查看此链接以获取解释


7
小nitpick:指针减法的结果为type ptrdiff_t。(通常在64位系统上,该类型将比更大int)。即使更改intptrdiff_t这段代码,如果arr占用了一半以上的地址空间,它仍然存在一个错误。
2014年

2
@MM另一个小问题:根据您的系统体系结构,地址空间不如大多数系统上的指针大小那么大。例如,Windows将64位应用程序的地址空间限制为8TB或44位。因此,例如,即使您的数组的地址空间大于4.1TB的一半,也不会有问题。只有在这些系统上您的地址空间超过63位时,才有可能遇到此类错误。通常,不用担心。
Aidiakapi'1

1
在32位x86 Linux或Windows上的@Aidiakapi(具有/3G选项),您可以将3G / 1G用户/内核分开,这使您可以将阵列大小最大为地址空间大小的75%。
罗斯兰

1
考虑foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];作为全局变量。 buf3[]声明失败,因为(&buf1)[1] - buf1它不是常量。
chux-恢复莫妮卡

2
这是技术上未定义的行为,因为该标准明确禁止取消引用数组的末尾(即使您不尝试读取存储的值)
MM


21

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

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
int noofele = sizeof(arr)/sizeof(int);仅比编码好一半int noofele = 9;sizeof(arr)阵列大小更改时,使用可保持灵活性。但是,sizeof(int)如果要arr[]更改类型,则需要更新。sizeof(arr)/sizeof(arr[0])即使类型众所周知,也最好使用。不清楚为什么使用intfor noofelevs. size_t,由返回的类型sizeof()
chux-恢复莫妮卡

19

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
感谢您的解释。原始版本会导致编译时错误。Clang报告“下标的值不是数组,指针或向量”。在这种情况下,这似乎是可取的行为,尽管您对宏中评估顺序的评论已被很好地接受。
马克·哈里森

1
我没有想到将编译器投诉作为错误类型的自动通知。谢谢!

3
有没有不使用的理由(sizeof (x) / sizeof (*x))
严重dev17年

16

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

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

等等,是无限的。


15
sizeof(array) / sizeof(array[0])

取决于类型array都有,你不需要使用sizeof(array) / sizeof(array[0]),如果array是数组要么charunsigned char或者signed char-从C18,6.5.3.4 / 4引用:“当sizeof会应用于具有char型,无符号的字符,或符号字符操作数,(或其限定版本)结果为1。” 在这种情况下,您可以sizeof(array)按照我专用的答案中的说明进行操作。
RobertS

15

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
奇怪的是,使用的代码size_t size_of_elementintint n = sizeof (a) / sizeof (a[0]); size_t n = sizeof (a) / sizeof (a[0]);
chux -恢复莫妮卡

1
@Yogeesh HT,您好,能否请您回答chux的疑问。我也很好奇知道int n = sizeof(a)/ sizeof(a [0])如何给出数组的长度以及为什么我们不使用size_t作为数组的长度。有人可以回答吗?
Brain

1
@Brain sizeof(a)给出数组中存在的所有元素的sizeof,sizeof(a [0])给出第1个元素的sizeof。假设a = {1,2,3,4,5}; sizeof(a)= 20bytes(如果sizeof(int)= 4bytes乘以5),sizeof(a [0])= 4bytes,所以20/4 = 5,即没有元素
Yogeesh HT

2
@YogeeshHT对于非常大的数组(如)char a[INT_MAX + 1u];int n使用in int n = sizeof (a) / sizeof (a[0]);不足(它是UB)。使用size_t n = sizeof (a) / sizeof (a[0]);不会引起此问题。
chux-恢复莫妮卡

13

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


TL; DR:

#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)[0]) * ARRAY_SIZE(arr))

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(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

有关此主题的重要错误: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_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);
}

更新(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()和一些海湾合作委员会的扩展:语句和声明中表达__builtin_types_compatible_p

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        sizeof(arr) / sizeof((arr)[0]);                                \
}                                                                       \
)

现在ARRAY_SIZE()是完全安全的,因此所有派生工具都将是安全的。


更新:libbsd提供__arraycount()

Libbsd提供了宏观__arraycount()<sys/cdefs.h>,这是不安全的,因为它缺少一对括号,但我们可以添加这些括号自己,所以我们甚至都不需要编写师在我们头(我们为什么要重复的代码已经存在? )。该宏是在系统头文件中定义的,因此,如果使用它,则必须使用上面的宏。

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "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))

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


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

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

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

#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)[0]) * ARRAY_SIZE(arr))

3
您介意解释为什么要投票吗?这显示了针对不安全且常见的构造(sizeof(arr))的解决方案,该构造未在其他地方显示:ARRAY_BYTES(arr)
卡卡休埃特·弗里托(Cacahuete Frito)

2
ARRAY_SIZE足够通用,可以自由使用,并且ARRAY_BYTES的名称非常明确,应该在ARRAY_SIZE旁边定义,以便用户可以轻松查看两者,以及其用法,我认为阅读该代码的人不会怀疑是的。我的意思是不使用simple sizeof,而是使用此构造;如果您想每次编写这些构造,都可能会犯一个错误(如果复制粘贴,这很常见,并且每次编写它们也很常见,因为它们都有很多括号)...
Cacahuete Frito

3
...,所以我站在主要结论上:一个单身sizeof显然是不安全的(答案在答案中),并且每次都不使用宏,而是使用我提供的构造更加不安全,所以唯一的方法是宏。
卡卡休埃特·弗里托(Cacahuete Frito)

3
@MarkHarrison我确实知道指针和数组之间的区别。但是有时候我有一个函数,后来我将其重构为小函数,首先是一个数组,然后是一个指针,这就是如果您忘记更改sizeof并拧紧它的地方,很容易看不到其中一个。
卡卡休埃特·弗里托(Cacahuete Frito)

3
@hyde另外,我知道这种区别并不意味着每个人都知道这种区别,为什么不使用基本上可以消除100%这些错误的东西呢?这个错误几乎使它进入了Linux。他说,它到达了Linus,这意味着它经过了很多审查,也意味着有可能同一错误已将其植入内核的另一部分Linux中。
卡卡休埃特·弗里托(Cacahuete Frito)

11

“您介绍了一种微妙的脚下射击方法”

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

#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。
Cacahuete Frito


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

1
无法升级strcpy(d.name, name);不处理溢出的代码。
chux-恢复莫妮卡

4

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

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

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


6
任何理由int elementssize_t elements
chux-恢复莫妮卡

3

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

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

2

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

#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

7
我没有投票,但这就像用砖头砸钉子一样,因为您没有注意到旁边有锤子。另外,人们倾向于不使用未初始化的变量...但是我想在这里它可以很好地满足您的目的。
德米特里(Dmitri)2014年

2
@Dmitri此处未访问未初始化的变量
MM

1
嗯 指针减法导致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]);就足够了。
chux-恢复莫妮卡

1
(char *)(&a+1)-(char *)a不是一个常数,即使大小固定,也可以在运行时进行计算a[10]sizeof(a)/sizeof(a[0])在这种情况下,在编译时保持不变。
chux-恢复莫妮卡

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

1

一个更优雅的解决方案是

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

0

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

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

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

引用C18,6.5.3.4 / 4:

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

因此,sizeof(a) / sizeof (a[0])相当于NUMBER OF ARRAY ELEMENTS / 1if a是type的数组charunsigned char或者signed char。除以1是多余的。

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

sizeof(a)

例如:

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

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


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


1
您可能还是希望对除法仍然应用宏,因为将来类型可能会更改(尽管可能不太可能),并且除法在编译时就已知道,因此编译器会对其进行优化(如果不更改,请先进行更改)您的编译器)。
Cacahuete Frito

1
@CacahueteFrito是的,与此同时,我也考虑过这一点。我把它作为答案的补充说明。谢谢。
RobertS支持Monica Cellio

-1

注意:这可以给您MM在注释中指出的未定义行为。

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

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


2
从技术上讲,这是不确定的行为;该*操作者可能不被施加到过去的最端指针
MM

3
“未定义的行为”表示C标准未定义行为。如果您在程序中尝试使用它,那么任何事情都可能发生
MM

@MM你说*(&a+1) - a;是不同于(&a)[1] - a;上面,不都 *(&a+1)(&a)[1]算作1过去的结束?
QuentinUK

@QuentinUK您的两个表达式都相同,x[y]定义为*(x + (y))
MM

@MM我是这么认为的。但是另一个答案是Arjun Sreedharan,它有38个向上箭头,而这个数字是-1。Arjun Sreedharan的答案没有提及不确定的行为。
QuentinUK
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.