簡體   English   中英

如何找到“sizeof”(指向數組的指針)?

[英]How to find the 'sizeof' (a pointer pointing to an array)?

首先,這里有一些代碼:

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 位系統上是四個字節)?

不,你不能。 編譯器不知道指針指向什么。 有一些技巧,比如用已知的帶外值結束數組,然后計算大小直到該值,但這不是使用sizeof()

另一個技巧是Zan提到的一個技巧,即將尺寸存儲在某處。 例如,如果您正在動態分配數組,請分配一個比您需要的大一個 int 的塊,將大小存儲在第一個 int 中,然后返回ptr+1作為指向數組的指針。 當您需要大小時,減小指針並查看隱藏的值。 請記住從頭開始釋放整個塊,而不僅僅是數組。

答案是不。”

C 程序員所做的是將數組的大小存儲在某處。 它可以是結構的一部分,或者程序員可以作弊,然后malloc()比請求的內存更多,以便在數組開始之前存儲長度值。

對於動態數組( 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++ 標准庫,比如vector,它更安全、更容易使用。

有一個使用 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

正如所有正確答案所述,您無法僅從數組的衰減指針值中獲取此信息。 如果衰減的指針是函數接收的參數,則必須以其他方式提供原始數組的大小,以便函數知道該大小。

這是一個與迄今為止提供的不同的建議,它將起作用:改為傳遞指向數組的指針。 此建議類似於 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 */

如果該函數應該能夠對任何大小的數組進行操作,那么您必須將大小作為附加信息提供給該函數。

沒有神奇的解決方案。 C 不是反射語言。 對象不會自動知道它們是什么。

但是你有很多選擇:

  1. 顯然,添加一個參數
  2. 將調用包裝在宏中並自動添加參數
  3. 使用更復雜的對象。 定義一個包含動態數組以及數組大小的結構。 然后,傳遞結構的地址。

對於這個具體的例子,是的,如果你使用 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

你可以這樣做:

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

我對這個問題的解決方案是將數組的長度保存到結構數組中,作為數組的元信息。

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

但是你必須關心設置你要存儲的數組的正確長度,因為沒有辦法檢查這個長度,就像我們的朋友大量解釋的那樣。

不,您不能使用sizeof(ptr)來查找數組ptr指向的大小。

如果您想將長度存儲在額外的空間中,雖然分配額外的內存(超過數組的大小)會很有幫助。

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 無論它指向什么。 因為指針通過存儲它的地址來指向其他元素。

這就是我個人在代碼中的做法。 我喜歡讓它盡可能簡單,同時仍然能夠獲得我需要的值。

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;
}
#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;
}
 #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'字符,因此可以使用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 );
}

大多數實現都會有一個函數告訴您使用malloc()calloc()分配的對象的保留大小,例如 GNU 有malloc_usable_size()

但是,這將返回反轉塊的大小,該大小可能大於malloc() / realloc()的值。


有一個流行的宏,您可以定義它來查找數組中的元素數量(Microsoft CRT 甚至為它提供了名稱為_countof的 OOB):

#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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM