'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

20
왜 안돼? 불필요한 괄호에 반대되는 것이 있습니까? 나는 그들 자신과 함께 조금 더 쉽게 읽는다고 생각합니다.
David Thornley

6
@Paul : well .. 그 호출의 왼쪽이 int에 대한 포인터라고 가정하면, int * ptr = malloc (4 * sizeof * ptr); 나에게 훨씬 더 명확합니다. 수학에서와 같이 읽고 읽을 수있는 문자가 적고 문자 상수를 앞으로 가져옵니다.
언 와인드

4
@unwind-int 배열을 의미 할 때 포인터 배열을 할당하지 마십시오!
Paul Tomblin

6
여기에는 "배열을 가리키는 포인터"가 없습니다. int를 가리키는 포인터입니다.
newacct February

답변:


269

아냐, 못해 컴파일러는 포인터가 무엇을 가리키는 지 모릅니다. 알려진 대역 외 값으로 배열을 끝내고 해당 값까지 크기를 세는 것과 같은 트릭이 있지만 사용하지는 않습니다 sizeof().

또 다른 트릭은 Zan이 언급 한 것으로 , 크기를 어딘가에 숨길 수 있습니다. 예를 들어 배열을 동적으로 할당하는 경우 필요한 것보다 1 int 큰 블록을 할당하고 첫 번째 int의 크기를 숨기고 ptr+1배열의 포인터로 반환 하십시오. 크기가 필요할 때 포인터를 줄이고 숨김 값을 살펴보십시오. 배열 만이 아니라 처음부터 전체 블록을 해제해야합니다.


12
이 글을 너무 늦게 게시해서 죄송합니다. 그러나 컴파일러가 포인터가 무엇을 가리키는 지 알지 못하면, 얼마나 많은 메모리를 지울 것인지를 어떻게 알 수 있습니까? 이 정보는 무료로 사용하는 것과 같은 기능을 위해 내부에 저장되어 있다는 것을 알고 있습니다. 그래서 내 질문은 왜 컴파일러가 그렇게 할 수 있습니까?
viki.omega9

11
free는 런타임에 크기를 감지하기 때문에 @ viki.omega9. 런타임 요소 (명령 줄 인수, 파일 내용, 달의 위상 등)에 따라 배열을 다른 크기로 만들 수 있으므로 컴파일러에서 크기를 알 수 없습니다.
Paul Tomblin

15
빠른 후속 조치, 왜 무료로 크기를 반환 할 수있는 기능이 없습니까?
viki.omega9

5
함수가 malloced 메모리로만 호출되고 라이브러리가 malloced 메모리를 가장 많이 본 방식으로 반환하면 (반환 포인터 앞에 int를 사용하여) 라이브러리를 작성할 수 있습니다. 그러나 포인터가 정적 배열 등을 가리키는 경우 실패합니다. 마찬가지로, malloced 메모리의 크기가 프로그램에서 액세스 가능하다는 보장은 없습니다.
Paul Tomblin

9
@ viki.omega9 : 명심해야 할 또 다른 사항은 malloc / free 시스템이 기록한 크기가 요청한 크기가 아닐 수 있다는 것입니다. 당신은 9 바이트를 malloc하고 16을 얻습니다. Malloc 3K 바이트와 4K를 얻습니다. 또는 비슷한 상황.
Zan Lynx

85

내 대답은 아니오 야."

C 프로그래머가하는 일은 배열의 크기를 어딘가에 저장하는 것입니다. 구조의 일부이거나 프로그래머가 malloc()배열 시작 전에 길이 값을 저장하기 위해 요청한 것보다 비트 및 더 많은 메모리를 속일 수 있습니다 .


3
파스칼 문자열이 구현되는 방식
dsm

6
그리고 파스칼 줄은 엑셀이 그렇게 빨리 달린 이유입니다!
Adam Naylor

8
@ 아담 : 빠르다. 내 문자열 구현 목록에서 사용합니다. 로드 크기, 프리 페치 pos + size, 검색 크기와 크기 비교, strncmp가 같으면 다음 문자열로 이동, 반복하기 때문에 선형 검색에 매우 빠릅니다. 이진 검색보다 최대 약 500 개의 문자열이 빠릅니다.
Zan Lynx

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.


11
ARRAY_SIZE는 실제 프로그래머가 사용하는 일반적인 패러다임입니다.
Sanjaya R

5
그렇습니다. 그것은 일반적인 패러다임입니다. 동적 배열에서 잊어 버리고 사용하기 쉽기 때문에 여전히 신중하게 사용해야합니다.
Ryan

2
예, 좋은 지적이지만 질문은 정적 배열이 아닌 포인터에 관한 것입니다.
Paul Tomblin

2
ARRAY_SIZE매크로는 인수가 배열 인 경우 항상 작동합니다 (예 : 배열 유형의 표현). 소위 "동적 배열"의 경우 실제 "배열"(배열 유형의 표현)을 얻지 못합니다. (물론, 배열 유형에는 컴파일 타임에 크기가 포함되기 때문에 할 수 없습니다.) 첫 번째 요소에 대한 포인터 만 가져옵니다. 여러분의 반대 의견은 "매개 변수가 실제로 정적 배열인지 확인하지 않습니다"는 실제로는 유효하지 않습니다. 하나는 배열이고 다른 하나는 그렇지 않기 때문입니다.
newacct

2
동일한 기능을 수행하지만 포인터 사용을 방해하는 템플릿 함수가 떠 있습니다.
Natalie Adams

18

sizeof () 를 사용하지 않고 C ++ 템플릿을 사용하는 깨끗한 솔루션이 있습니다 . 다음 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

2
c ++을 사용하고 실제로 배열 유형의 변수가 있으면 좋습니다. 언어는 C이고 OP가 배열 크기를 얻으려는 것은 간단한 포인터입니다.
Oguk

이 코드가 다른 크기 / 유형 조합마다 동일한 코드를 다시 작성하여 코드 팽창을 유발합니까, 아니면 컴파일러가 존재하지 않는 마법으로 최적화 되었습니까?
user2796283

@WorldSEnder : 배열 유형 (변수 이름이없고 크기와 요소 유형 만 있음)의 참조를위한 C ++ 구문입니다.
Peter Cordes

@ user2796283 :이 함수는 컴파일 타임에 완전히 최적화됩니다. 마술이 필요하지 않습니다. 단일 정의에 아무것도 결합하지 않고 단순히 컴파일 타임 상수로 인라인합니다. (그러나 디버그 빌드에서는 서로 다른 상수를 반환하는 여러 개의 개별 함수가 있습니다. 링커 매직은 동일한 상수를 사용하는 함수를 병합 할 수 있습니다. 호출자는 SIZE인수로 전달하지 않습니다. 이는 템플릿 매개 변수입니다. 함수 정의에 의해 이미 알려질 것입니다.)
Peter Cordes

5

이 특정 예제의 경우 typedef를 사용하는 경우 (아래 참조) 있습니다. 물론 이렇게하면 포인터가 무엇을 가리키는 지 알기 때문에 SIZEOF_DAYS를 사용하는 것이 좋습니다.

malloc () 등에 의해 리턴 된 것처럼 (void *) 포인터가 있다면, 아니오, 포인터가 가리키는 데이터 구조를 판별 할 방법이 없으므로 그 크기를 결정할 방법이 없습니다.

#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을 해결하십시오.
Max

4

마법의 해결책은 없습니다. C는 반사 언어가 아닙니다. 개체는 자신이 무엇인지 자동으로 알지 못합니다.

그러나 많은 선택이 있습니다.

  1. 분명히, 매개 변수를 추가
  2. 매크로로 통화를 감싸고 매개 변수를 자동으로 추가
  3. 더 복잡한 개체를 사용하십시오. 동적 배열과 배열의 크기를 포함하는 구조를 정의하십시오. 그런 다음 구조의 주소를 전달하십시오.

객체는 자신이 무엇인지 알고 있습니다. 당신은 하위 객체를 가리키는 경우에, 전체 개체 또는 더 큰 하위 객체에 대한 정보를 얻는 방법은 없다
MM

2

이 문제에 대한 나의 해결책은 배열의 길이를 배열에 대한 메타 정보로 구조체 배열에 저장하는 것입니다.

#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)는 포인터의 크기입니다.
Amitābha

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가 크기 변수로 전달됩니다 .

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

코드는 주석으로 가득 차 있지만 코드 외부에서 이것이 어떻게 작동하는지에 대한 일반적인 설명을 일반 텍스트로 추가하면 모든 것이 더 쉬워 질 것이라고 생각합니다. 당신은 기쁘게 할 수 편집 을 질문을하고 있습니까? 감사합니다!
Fabio는 Reinstate Monica

선형 적으로 검색 할 수 있도록 각 요소에 대한 포인터 배열을 작성 NULL하는 것은 별개의 size직접 저장하는 것으로 상상할 수있는 가장 효율적인 대안 일 것입니다 . 특히 실제로 경우에 사용하는 간접이 한층 모든 시간을.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.