如何找到数组的大小(从指向第一个元素数组的指针)?

How to find the size of an array (from a pointer pointing to the first element array)?

提问人:jkidv 提问时间:1/30/2009 最后编辑:cigienjkidv 更新时间:10/23/2023 访问量:536971

问:

首先,这里有一些代码:

int main() 
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", sizeof(days));
    printf("%u\n", sizeof(ptr));

    return 0;
}

有没有办法找出指向的数组的大小(而不是仅仅给出它的大小,在 32 位系统上是四个字节)?ptr

C++ 数组 C 指针 sizeof

评论

94赞 Paul Tomblin 1/30/2009
我一直使用带有 sizeof 的 parens - 当然它使它看起来像一个函数调用,但我认为它更清晰。
21赞 David Thornley 1/30/2009
为什么不呢?你对多余的括号有什么反对吗?我认为和他们一起读起来更容易一些,我自己。
6赞 unwind 1/30/2009
@Paul:嗯..假设该调用的左侧是指向 int 的指针,我会将其写为 int *ptr = malloc(4 * sizeof *ptr);这对我来说要清楚得多。少读,把字面上的常数放在前面,就像在数学中一样。
4赞 Paul Tomblin 1/30/2009
@unwind - 当你指的是一个整数数组时,不要分配指针数组!
6赞 newacct 3/1/2013
这里没有“指向数组的指针”。只是一个指向 int 的指针。

答:

343赞 Paul Tomblin 1/30/2009 #1

不,你不能。编译器不知道指针指向什么。有一些技巧,例如以已知的带外值结束数组,然后计算大小直到该值,但这没有使用 .sizeof()

另一个技巧是 Zan 提到的那个,那就是将大小藏在某个地方。例如,如果您要动态分配数组,请分配一个比您需要的块大一个的块,将大小存放在那里,然后作为指向数组的指针返回。当您需要大小时,请递减指针并查看隐藏的值。只要记住从头开始释放整个块,而不仅仅是数组。size_tptr+sizeof(size_t)

评论

22赞 viki.omega9 3/4/2013
很抱歉这么晚才发表评论,但如果编译器不知道指针指向什么,free 怎么知道要清除多少内存?我确实知道这些信息存储在内部,用于免费使用等功能。所以我的问题是为什么编译器也可以这样做?
18赞 Paul Tomblin 3/4/2013
@viki.omega9,因为 free 会在运行时发现大小。编译器无法知道大小,因为您可以根据运行时因素(命令行参数、文件内容、月相等)使数组具有不同的大小。
21赞 viki.omega9 3/4/2013
快速跟进,为什么没有一个函数可以像 free 那样返回大小?
5赞 Paul Tomblin 3/4/2013
好吧,如果你能保证该函数只使用错位内存调用,并且库以我见过的大多数方式跟踪错位内存(通过在返回的指针之前使用 int),那么你可以编写一个。但是,如果指针指向静态数组或类似数组,则它将失败。同样,也不能保证程序可以访问错位内存的大小。
15赞 Zan Lynx 7/20/2014
@viki.omega9:要记住的另一件事是,malloc/free 系统记录的大小可能不是您要求的大小。你 malloc 9 字节得到 16 个字节。Malloc 3K 字节并获得 4K。或类似情况。
116赞 Zan Lynx 1/30/2009 #2

答案是“不”。

C 程序员所做的是将数组的大小存储在某个地方。它可以是结构的一部分,或者程序员可以欺骗比请求的更多的内存,以便在数组开始之前存储长度值。malloc()

评论

3赞 dsm 1/30/2009
这就是 pascal 字符串的实现方式
8赞 Adam Naylor 7/15/2010
显然,Pascal 字符串是 Excel 运行速度如此之快的原因!
8赞 Zan Lynx 7/15/2010
@Adam:速度很快。我在我的字符串实现列表中使用它。线性搜索速度超快,因为它是:加载大小、预取 pos+大小、将大小与搜索大小进行比较,如果等于 strncmp,移动到下一个字符串,重复。它比二进制搜索更快,最多可以搜索大约 500 个字符串。
57赞 Ryan 1/30/2009 #3

对于动态数组(malloc 或 C++ new),您需要存储其他人提到的数组大小,或者可能构建一个处理添加、删除、计数等的数组管理器结构。 不幸的是,C 在这方面做得不如 C++,因为您基本上必须为要存储的每种不同数组类型构建它,如果您需要管理多种类型的数组,这很麻烦。

对于静态数组,例如示例中的数组,有一个通用宏用于获取大小,但不建议这样做,因为它不会检查参数是否真的是静态数组。不过,该宏用于实际代码中,例如在 Linux 内核头文件中,尽管它可能与下面的宏略有不同:

#if !defined(ARRAY_SIZE)
    #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif

int main()
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", ARRAY_SIZE(days));
    printf("%u\n", sizeof(ptr));
    return 0;
}

你可以谷歌一下,有理由对这样的宏保持警惕。小心。

如果可能的话,C++ stdlib 如 vector 更安全、更易于使用。

评论

13赞 Sanjaya R 1/30/2009
ARRAY_SIZE是各地实用程序员使用的通用范式。
6赞 Ryan 1/30/2009
是的,这是一个常见的范式。不过,您仍然需要谨慎使用它,因为它很容易忘记并在动态数组上使用它。
2赞 Paul Tomblin 1/30/2009
是的,很好,但被问到的问题是关于指针的,而不是静态数组的。
2赞 newacct 3/1/2013
如果该宏的参数是数组(即数组类型的表达式),则该宏始终有效。对于所谓的“动态数组”,你永远不会得到一个实际的“数组”(数组类型的表达式)。(当然,你不能,因为数组类型在编译时包括它们的大小。你只需要得到一个指向第一个元素的指针。您的反对意见“不检查参数是否真的是静态数组”是无效的,因为它们是不同的,因为一个是数组,另一个不是。ARRAY_SIZE
4赞 Natalie Adams 4/23/2013
有一个模板函数可以执行相同的操作,但会阻止使用指针。
6赞 David 4/14/2011 #4

对于这个特定示例,是的,如果您使用 typedefs(见下文),则有。当然,如果你这样做,你就可以很好地使用SIZEOF_DAYS,因为你知道指针指向什么。

如果你有一个 (void *) 指针,正如 malloc() 或类似文件返回的那样,那么,不,没有办法确定指针指向什么数据结构,因此,没有办法确定它的大小。

#include <stdio.h>

#define NUM_DAYS 5
typedef int days_t[ NUM_DAYS ];
#define SIZEOF_DAYS ( sizeof( days_t ) )

int main() {
    days_t  days;
    days_t *ptr = &days; 

    printf( "SIZEOF_DAYS:  %u\n", SIZEOF_DAYS  );
    printf( "sizeof(days): %u\n", sizeof(days) );
    printf( "sizeof(*ptr): %u\n", sizeof(*ptr) );
    printf( "sizeof(ptr):  %u\n", sizeof(ptr)  );

    return 0;
} 

输出:

SIZEOF_DAYS:  20
sizeof(days): 20
sizeof(*ptr): 20
sizeof(ptr):  4
19赞 skurton 4/19/2012 #5

有一个使用 C++ 模板的干净解决方案,而无需使用 .以下函数返回任何静态数组的大小:sizeofgetSize()

#include <cstddef>

template<typename T, std::size_t SIZE>
constexpr std::size_t getSize(T (&)[SIZE]) {
    return SIZE;
}

下面是一个具有foo_t结构的示例:

#include <cstddef>
#include <cstdio>

template<typename T, std::size_t SIZE>
constexpr std::size_t getSize(T (&)[SIZE]) {
    return SIZE;
}

struct foo_t {
    int ball;
};

int main()
{
    foo_t foos3[] = {{1},{2},{3}};
    foo_t foos5[] = {{1},{2},{3},{4},{5}};
    std::printf("%u\n", getSize(foos3));
    std::printf("%u\n", getSize(foos5));
}

输出:

3
5

评论

1赞 WorldSEnder 6/4/2014
我从未见过符号.你能解释一下这意味着什么吗?您也可以在此上下文中提及 constepr。T (&)[SIZE]
7赞 Oguk 10/12/2014
如果你使用 c++,并且你实际上有一个数组类型的变量,那就太好了。问题中都不是这种情况:语言是 C,OP 想要从中获取数组大小的是一个简单的指针。
0赞 user2796283 8/28/2016
此代码是否会通过为每个不同的大小/类型组合重新创建相同的代码而导致代码膨胀,或者编译器是否神奇地优化了它?
0赞 Peter Cordes 6/19/2018
@WorldSEnder:这是数组类型引用的C++语法(没有变量名称,只有大小和元素类型)。
0赞 Peter Cordes 6/19/2018
@user2796283:此函数在编译时完全优化;不需要魔法;它不是将任何东西组合到一个定义中,它只是将其内联到一个编译时常量中。(但是在调试版本中,是的,你会有一堆单独的函数,它们返回不同的常量。链接器魔术可能会合并使用相同常量的链接器。调用方不作为 arg 传递,它是一个模板参数,函数定义必须已经知道它。SIZE
11赞 jxh 4/10/2013 #6

正如所有正确答案所述,您不能仅从数组的衰减指针值中获取此信息。如果衰减的指针是函数接收的参数,则必须以其他方式提供原始数组的大小,以便函数知道该大小。

这里有一个与目前提供的建议不同的建议,它将起作用:将指针传递给数组。此建议类似于 C++ 样式建议,只是 C 不支持模板或引用:

#define ARRAY_SZ 10

void foo (int (*arr)[ARRAY_SZ]) {
    printf("%u\n", (unsigned)sizeof(*arr)/sizeof(**arr));
}

但是,这个建议对于您的问题来说有点愚蠢,因为该函数被定义为确切地知道传入的数组的大小(因此,几乎不需要在数组上使用 sizeof)。不过,它所做的是提供某种类型的安全性。它将禁止您传入不需要大小的数组。

int x[20];
int y[10];
foo(&x); /* error */
foo(&y); /* ok */

如果该函数应该能够对任何大小的数组进行操作,则必须向该函数提供大小作为附加信息。

0赞 user3065147 3/11/2014 #7
 #define array_size 10

 struct {
     int16 size;
     int16 array[array_size];
     int16 property1[(array_size/16)+1]
     int16 property2[(array_size/16)+1]
 } array1 = {array_size, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

 #undef array_size

array_size传递给 size 变量:

#define array_size 30

struct {
    int16 size;
    int16 array[array_size];
    int16 property1[(array_size/16)+1]
    int16 property2[(array_size/16)+1]
} array2 = {array_size};

#undef array_size

用法是:

void main() {

    int16 size = array1.size;
    for (int i=0; i!=size; i++) {

        array1.array[i] *= 2;
    }
}
3赞 user4713908 10/5/2015 #8

我对这个问题的解决方案是将数组的长度保存到结构数组中,作为有关数组的元信息。

#include <stdio.h>
#include <stdlib.h>

struct Array
{
    int length;

    double *array;
};

typedef struct Array Array;

Array* NewArray(int length)
{
    /* Allocate the memory for the struct Array */
    Array *newArray = (Array*) malloc(sizeof(Array));

    /* Insert only non-negative length's*/
    newArray->length = (length > 0) ? length : 0;

    newArray->array = (double*) malloc(length*sizeof(double));

    return newArray;
}

void SetArray(Array *structure,int length,double* array)
{
    structure->length = length;
    structure->array = array;
}

void PrintArray(Array *structure)
{       
    if(structure->length > 0)
    {
        int i;
        printf("length: %d\n", structure->length);
        for (i = 0; i < structure->length; i++)
            printf("%g\n", structure->array[i]);
    }
    else
        printf("Empty Array. Length 0\n");
}

int main()
{
    int i;
    Array *negativeTest, *days = NewArray(5);

    double moreDays[] = {1,2,3,4,5,6,7,8,9,10};

    for (i = 0; i < days->length; i++)
        days->array[i] = i+1;

    PrintArray(days);

    SetArray(days,10,moreDays);

    PrintArray(days);

    negativeTest = NewArray(-5);

    PrintArray(negativeTest);

    return 0;
}

但是你必须关心设置你想要存储的数组的正确长度,因为没有办法检查这个长度,就像我们的朋友大量解释的那样。

1赞 SKD 3/15/2016 #9

不可以,你不能用它来查找数组所指向的大小。sizeof(ptr)ptr

尽管如果要将长度存储在额外的空间中,分配额外的内存(大于数组的大小)将会有所帮助。

1赞 this.shivi 7/10/2016 #10
int main() 
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", sizeof(days));
    printf("%u\n", sizeof(ptr));

    return 0;
}

days[] 的大小为 20,即元素数 * 数据类型的大小。 而指针的大小是 4,无论它指向什么。 因为指针通过存储其地址指向其他元素。

评论

1赞 王奕然 12/27/2016
sizeof(ptr) 是指针的大小,sizeof(*ptr) 是指向的指针的大小
5赞 DigitalRoss 7/26/2016 #11

没有神奇的解决方案。C 不是一种反思性语言。对象不会自动知道它们是什么。

但是您有很多选择:

  1. 显然,添加一个参数
  2. 将调用包装在宏中并自动添加参数
  3. 使用更复杂的对象。定义一个包含动态数组和数组大小的结构。然后,传递结构的地址。

评论

0赞 M.M 1/29/2020
对象知道它们是什么。但是,如果指向子对象,则无法获取有关完整对象或较大子对象的信息
1赞 baz 3/11/2017 #12

在字符串中,末尾有一个字符,因此可以使用类似 的函数来获取字符串的长度。例如,整数数组的问题在于,您不能使用任何值作为结束值,因此一种可能的解决方案是寻址数组并将指针用作结束值。'\0'strlenNULL

#include <stdio.h>
/* the following function will produce the warning:
 * ‘sizeof’ on array function parameter ‘a’ will
 * return size of ‘int *’ [-Wsizeof-array-argument]
 */
void foo( int a[] )
{
    printf( "%lu\n", sizeof a );
}
/* so we have to implement something else one possible
 * idea is to use the NULL pointer as a control value
 * the same way '\0' is used in strings but this way
 * the pointer passed to a function should address pointers
 * so the actual implementation of an array type will
 * be a pointer to pointer
 */
typedef char * type_t; /* line 18 */
typedef type_t ** array_t;
int main( void )
{
    array_t initialize( int, ... );
    /* initialize an array with four values "foo", "bar", "baz", "foobar"
     * if one wants to use integers rather than strings than in the typedef
     * declaration at line 18 the char * type should be changed with int
     * and in the format used for printing the array values 
     * at line 45 and 51 "%s" should be changed with "%i"
     */
    array_t array = initialize( 4, "foo", "bar", "baz", "foobar" );

    int size( array_t );
    /* print array size */
    printf( "size %i:\n", size( array ));

    void aprint( char *, array_t );
    /* print array values */
    aprint( "%s\n", array ); /* line 45 */

    type_t getval( array_t, int );
    /* print an indexed value */
    int i = 2;
    type_t val = getval( array, i );
    printf( "%i: %s\n", i, val ); /* line 51 */

    void delete( array_t );
    /* free some space */
    delete( array );

    return 0;
}
/* the output of the program should be:
 * size 4:
 * foo
 * bar
 * baz
 * foobar
 * 2: baz
 */
#include <stdarg.h>
#include <stdlib.h>
array_t initialize( int n, ... )
{
    /* here we store the array values */
    type_t *v = (type_t *) malloc( sizeof( type_t ) * n );
    va_list ap;
    va_start( ap, n );
    int j;
    for ( j = 0; j < n; j++ )
        v[j] = va_arg( ap, type_t );
    va_end( ap );
    /* the actual array will hold the addresses of those
     * values plus a NULL pointer
     */
    array_t a = (array_t) malloc( sizeof( type_t *) * ( n + 1 ));
    a[n] = NULL;
    for ( j = 0; j < n; j++ )
        a[j] = v + j;
    return a;
}
int size( array_t a )
{
    int n = 0;
    while ( *a++ != NULL )
        n++;
    return n;
}
void aprint( char *fmt, array_t a )
{
    while ( *a != NULL )
        printf( fmt, **a++ );   
}
type_t getval( array_t a, int i )
{
    return *a[i];
}
void delete( array_t a )
{
    free( *a );
    free( a );
}

评论

0赞 Fabio says Reinstate Monica 3/11/2017
您的代码充满了注释,但我认为如果您在代码之外添加一些关于其工作原理的一般解释,作为普通文本,这将使一切变得更容易。你能编辑你的问题并去做吗?谢谢!
0赞 Peter Cordes 6/19/2018
创建一个指向每个元素的指针数组,以便您可以对其进行线性搜索,这可能是可以想象到的效率最低的替代方法,而不是直接存储单独的元素。特别是如果你实际上一直在使用这个额外的间接层。NULLsize
6赞 Tᴏᴍᴇʀ Wᴏʟʙᴇʀɢ 6/22/2018 #13

你可以做这样的事情:

int days[] = { /*length:*/5, /*values:*/ 1,2,3,4,5 };
int *ptr = days + 1;
printf("array length: %u\n", ptr[-1]);
return 0;
2赞 Mohit Tomar 3/15/2021 #14

这就是我个人在我的代码中的做法。我喜欢让它尽可能简单,同时仍然能够获得我需要的值。

typedef struct intArr {
    int size;
    int* arr; 
} intArr_t;

int main() {
    intArr_t arr;
    arr.size = 6;
    arr.arr = (int*)malloc(sizeof(int) * arr.size);

    for (size_t i = 0; i < arr.size; i++) {
        arr.arr[i] = i * 10;
    }

    return 0;
}

评论

0赞 David Ranieri 3/15/2021
更喜欢存储大小。size_t
0赞 Amarok24 9/8/2022
这是一个非常好和简单的方法!顺便说一句,结构后面的可以省略。此外,编写 malloc 行的一种更短、更易读的方式是,这使得它更易于重用,因为您不需要指定“int”。intArrarr.arr = malloc(arr.size * sizeof *arr.arr);
0赞 12431234123412341234123 8/4/2021 #15

大多数实现都会有一个函数,告诉你用 或 分配的对象的保留大小,例如 GNU 有malloc()calloc()malloc_usable_size()

但是,这将返回反转块的大小,该大小可能大于给定给 / 的值。malloc()realloc()


0赞 ivan.ukr 12/10/2021 #16

有一个流行的宏,您可以定义它来查找数组中的元素数量(Microsoft CRT 甚至为其提供了 OOB 名称):_countof

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

然后你可以写:

int my_array[] = { ... some elements ... };
printf("%zu", countof(my_array)); // 'z' is correct type specifier for size_t
1赞 Doerthous 3/18/2022 #17
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#define array(type) struct { size_t size; type elem[0]; }

void *array_new(int esize, int ecnt)
{
    size_t *a = (size_t *)malloc(esize*ecnt+sizeof(size_t));
    if (a) *a = ecnt;
    return a;
}
#define array_new(type, count) array_new(sizeof(type),count)
#define array_delete free
#define array_foreach(type, e, arr) \
    for (type *e = (arr)->elem; e < (arr)->size + (arr)->elem; ++e)

int main(int argc, char const *argv[])
{
    array(int) *iarr = array_new(int, 10);
    array(float) *farr = array_new(float, 10);
    array(double) *darr = array_new(double, 10);
    array(char) *carr = array_new(char, 11);
    for (int i = 0; i < iarr->size; ++i) {
        iarr->elem[i] = i;
        farr->elem[i] = i*1.0f;
        darr->elem[i] = i*1.0;
        carr->elem[i] = i+'0';
    }
    array_foreach(int, e, iarr) {
        printf("%d ", *e);
    }
    array_foreach(float, e, farr) {
        printf("%.0f ", *e);
    }
    array_foreach(double, e, darr) {
        printf("%.0lf ", *e);
    }
    carr->elem[carr->size-1] = '\0';
    printf("%s\n", carr->elem);

    return 0;
}