如何找到“ sizeof”(指向数组的指针)?


309

首先,这是一些代码:

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

    return 0;
}

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


84
我一直使用带有sizeof的parens-确保它看起来像一个函数调用,但我认为它更清晰。
Paul Tomblin,2009年

20
为什么不?您是否有反对多余括号的内容?我认为与他们本人一起阅读起来会容易一些。
David Thornley,

6
@Paul:好吧..假设该调用的左侧是指向int的指针,我将其写为int * ptr = malloc(4 * sizeof * ptr); 对我来说,这要清楚得多。减少阅读时间,像数学一样将常量常量置于最前面。
放松

4
@unwind-当您要使用整数数组时,请勿分配指针数组!
Paul Tomblin,2009年

6
这里没有“指向数组的指针”。只是一个指向int的指针。
newacct

Answers:


269

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

Zan提到的另一个技巧是将大小存储在某个地方。例如,如果要动态分配数组,则将一个块分配一个比您所需的块大一个int的块,将大小存储在第一个int中,然后ptr+1作为指向数组的指针返回。当需要大小时,减少指针并偷看隐藏的值。只要记住要从头开始释放整个块,而不仅仅是数组。


12
对于这么晚发布评论,我感到抱歉,但是如果编译器不知道指针指向的内容,那么free怎么知道要清除多少内存呢?我确实知道此信息是内部存储的,以便免费使用。所以我的问题是,为什么编译器也可以这样做?
viki.omega9,2013年

11
@ viki.omega9,因为free在运行时发现了大小。编译器无法知道大小,因为您可以根据运行时因素(命令行参数,文件内容,月相等)使数组具有不同的大小。
Paul Tomblin

15
快速跟进,为什么没有一个可以免费返回大小的函数?
viki.omega9,2013年

5
好吧,如果可以保证只用分配的内存调用该函数,并且库以我所见过的大多数方式(通过在返回的指针之前使用int)跟踪分配的内存,则可以编写一个。但是,如果指针指向静态数组等,它将失败。同样,不能保证程序可以访问已分配内存的大小。
Paul Tomblin

9
@ viki.omega9:要记住的另一件事是,malloc / free系统记录的大小可能不是您要求的大小。您malloc 9个字节,得到16。Malloc 3K字节,得到4K。或类似情况。
Zan Lynx 2014年

85

答案是不。”

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


3
那就是pascal字符串的实现方式
dsm

6
显然,pascal字符串是excel运行这么快的原因!
亚当·内洛

8
@亚当:很快。我在我的字符串实现列表中使用它。进行线性搜索非常快,因为它是:加载大小,预取pos +大小,将大小与搜索大小进行比较,如果strncmp相等,则移至下一个字符串,然后重复。它比二进制搜索(最多约500个字符串)要快。
Zan Lynx'7

47

对于动态数组(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;
}

您可以出于某些原因对此类宏保持警惕,因此可以使用Google。小心。

如果可能的话,使用C ++ stdlib(例如vector)会更安全,更易于使用。


11
ARRAY_SIZE是世界各地的实用程序员常用的范例。
Sanjaya R

5
是的,这是一个常见的范例。您仍然需要谨慎使用它,因为它很容易忘记并在动态数组上使用。
Ryan

2
是的,很好,但是要问的问题是关于指针一,而不是静态数组一。
Paul Tomblin,2009年

2
ARRAY_SIZE宏总是工作如果它的参数是一个数组(数组类型的即表达)。对于您所谓的“动态数组”,您永远不会获得实际的“数组”(数组类型的表达)。(当然,您不能这样做,因为数组类型在编译时会包括它们的大小。)您只需获得一个指向第一个元素的指针即可。您的反对意见“不检查参数是否真的是静态数组”不是真的有效,因为它们是不同的,因为一个是数组而另一个不是。
newacct

2
有一个浮动的模板函数可以执行相同的操作,但是会阻止使用指针。
Natalie Adams

18

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

#include <cstddef>

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

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

#include <cstddef>

template<typename T, size_t SIZE>
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}};
    printf("%u\n", getSize(foos3));
    printf("%u\n", getSize(foos5));

    return 0;
}

输出:

3
5

我从未见过这种记号T (&)[SIZE]。您能解释一下这是什么意思吗?您也可以在这种情况下提及constexpr。
WorldSEnder 2014年

2
如果使用c ++且实际上具有数组类型的变量,那很好。问题都不是:语言是C,OP想要从中获取数组大小的东西是一个简单的指针。
Oguk 2014年

通过为每个不同的大小/类型组合重新创建相同的代码,此代码会导致代码膨胀吗?还是编译器对其进行了神奇的优化?
user2796283

@WorldSEnder:这是引用数组类型(没有变量名,只是大小和元素类型)的C ++语法。
彼得·科德斯

@ user2796283:此功能在编译时已完全优化;不需要魔法;它没有将任何内容组合到一个定义中,只是将其内联到一个编译时常量中。(但是,在调试版本中,是的,您会有很多单独的函数返回不同的常量。链接器魔术可能会合并使用相同常量的函数。调用方不会SIZE作为arg 传递,这是一个模板参数,具有已经通过函数定义是已知的)。
彼得·科德斯

5

对于此特定示例,是的,如果您使用typedef(请参见下文)。当然,如果以这种方式进行操作,那么您就可以使用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

5

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

这是与到目前为止所提供的建议不同的建议,它将起作用:将指针传递给数组。该建议与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 */

如果该函数应该能够在任何大小的数组上运行,那么您将必须为函数提供大小作为附加信息。


1
为“您无法单独从数组的衰减指针值获取此信息” +1,并提供了一种解决方法。
2013年

4

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

但是您有很多选择:

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

对象知道它们是什么。但是,如果您指向一个子对象,则无法获取有关完整对象或更大子对象的信息
MM

2

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

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

但是,您必须关心设置要存储的数组的正确长度,因为就像我们的朋友大量解释的那样,这是无法检查该长度的方法。


2

您可以执行以下操作:

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

1

不,您不能sizeof(ptr)用来查找ptr指向的数组大小。

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


1
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
的sizeof(PTR)是指针和sizeof(* PTR)的尺寸指针的哪个大小
阿弥陀佛

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

0

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

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

您的代码充满了注释,但是我认为,如果您添加一些常规解释(如普通文本),它将使一切变得更容易。您能编辑问题并做吗?谢谢!
法比奥称莫妮卡(Monica)恢复职时间为

创建指向每个元素的指针数组,以便可以对其进行线性搜索,这NULL可能是仅size直接存储单独元素的效率最低的替代方法。特别是如果您一直在使用此额外的间接层。
彼得·科德斯
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.