C에서 배열의 크기를 어떻게 결정합니까?


답변:


1260

행정상 개요 :

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

전체 답변 :

바이트 단위로 배열의 크기를 결정하려면 sizeof 연산자를 사용할 수 있습니다 .

int a[17];
size_t n = sizeof(a);

내 컴퓨터에서 ints의 길이는 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의 결과는 컴파일 타임 상수로 정의되므로 모든 컴파일러의 경우에 해당합니다.
마크 해리슨

450
중요 : 여기서 읽는 것을 멈추지 말고 다음 답변을 읽으십시오! 이것은 malloc ()을 사용하거나 함수 매개 변수에 액세스하는 경우 스택의 배열에만 작동합니다 . 운이 좋지 않습니다. 아래를 참조하십시오.
Markus

7
C 또는 C ++의 Windows API 프로그래밍의 경우 ARRAYSIZE정의 된 마크로가 있습니다 WinNT.h(다른 헤더에 의해 가져옴). 따라서 WinAPI 사용자는 자신의 마크로를 정의 할 필요가 없습니다.
Lumi

17
@Markus 배열 유형을 가진 모든 변수에 대해 작동합니다. "스택"일 필요는 없습니다. 예 static int a[20];. 그러나 귀하의 의견은 배열과 포인터의 차이를 인식하지 못하는 독자에게 유용합니다.
MM

807

sizeof방법은 올바른 방법입니다 IFF 는 매개 변수로받지 배열을 다루고있다. 함수에 매개 변수로 전송 된 배열은 포인터로 취급되므로 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 OS) :

sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2

출력 (32 비트 Windows OS에서) :

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))이지만 int는 여전히 4 주 길이 (sizeof (intArray [0]))입니다.
Elideb

13
@Pacerier : 올바른 코드가 없습니다. 일반적인 해결책은 배열과 함께 길이를 별도의 인수로 전달하는 것입니다.
Jean Hominal

10
잠깐, 포인터에서 직접 배열에 액세스하고 크기를 볼 수있는 방법이 없습니까? C가 처음입니다.
sudo

7
@Michael Trouw : 기분이 나아지면 연산자 구문을 사용할 수 있습니다 (sizeof array / sizeof *array).
chqrlie

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 (signed char)는 다음과 같습니다. 1; 다른 기본 유형에 적용된 sizeof의 결과는 구현에 따라 정의됩니다. "
Skizz September

1.6 절 C ++ 메모리 모델 : "C ++ 메모리 모델의 기본 저장 단위는 바이트입니다. 바이트는 기본 실행 문자 세트의 멤버를 포함 할 수있을만큼 충분히 크며 연속적인 비트 시퀀스 (숫자)로 구성됩니다. "구현이 정의되어 있습니다."
Skizz September

2
CRAY는 char32 비트의 C를 가졌음을 기억합니다 . 모든 표준에 따르면 0에서 127 사이의 정수 값을 나타낼 수 있으며 범위는 -127에서 127까지 (char는 부호 있음) 또는 0에서 255는 (char는 부호 없음)입니다.
vonbrand

5
이것은 훌륭한 응답입니다. 위의 모든 어설 션이 TRUE로 평가되었다고 언급하고 싶습니다.
Javad

49

"속임수"의 크기는 내가 아는 가장 좋은 방법이며, 괄호 사용에있어 작은 (하지만 나에게는 이것이 주요 애완 동물)이다.

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

이제 당신은 보호됩니다. 물론 변수의 이름을 복제하지만 변수를 변경하면 컴파일러가 감지 할 수있는 방식으로 침입 할 가능성이 높습니다.


Btw, 프로세서 수준에서 동일한 명령입니까? sizeof(int)보다 적은 지시가 필요 합니까 sizeof(foo)?
Pacerier

@Pacerier : 아니요. 동일합니다. int x = 1+1;대 생각하십시오 int x = (1+1);. 여기서 괄호는 순전히 미적입니다.
quetzalcoatl

@Aidiakapi 사실이 아닙니다. C99 VLA를 고려하십시오.

@unwind 감사합니다. 내 의견을 수정하려면 sizeofC ++ 및 C89에서 항상 일정합니다. C99의 가변 길이 배열을 사용하면 런타임에 평가할 수 있습니다.
Aidiakapi

2
sizeof연산자 일 수 있지만 Linus Torvalds에 따라 함수로 취급해야합니다. 동의한다. 그의 합리적인 설명을 여기에서 읽으십시오 : lkml.org/lkml/2012/7/11/103
Dr. Person Person II

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

설명을 보려면 이 링크 를 확인하십시오.


7
작은 nitpick : 포인터 빼기의 결과는 type ptrdiff_t입니다. 일반적으로 64 비트 시스템에서는이 유형이보다 큽니다 int. 변경하더라도 intptrdiff_t이 코드에 있다면, 그것은 여전히 버그가 arr주소 공간의 절반 이상을 차지한다.
MM

2
@MM 또 다른 작은 의미 : 시스템 아키텍처에 따라 주소 공간은 대부분의 시스템에서 포인터 크기만큼 크지 않습니다. 예를 들어 Windows는 64 비트 응용 프로그램의 주소 공간을 8TB 또는 44 비트로 제한합니다. 예를 들어 주소 공간이 4.1TB의 절반보다 큰 어레이를 사용하더라도 버그는 아닙니다. 해당 시스템에서 주소 공간이 63 비트를 초과하는 경우에만 이러한 버그가 발생할 수 있습니다. 일반적으로 걱정하지 마십시오.
Aidiakapi

1
32 비트 x86 Linux 또는 Windows의 경우 @Aidiakapi /3G옵션을 사용하면 3G / 1G 사용자 / 커널 분할이 가능하여 주소 공간 크기의 최대 75 %까지 배열 크기를 가질 수 있습니다.
Ruslan

1
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];전역 변수로 고려하십시오 . 상수가 buf3[]아니므로 선언이 실패 (&buf1)[1] - buf1합니다.
chux-복원 Monica Monica

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])유형이 잘 알려진 경우에도 마찬가지입니다. 사용하는 이유는 불분명 int위한 noofelesize_t, 유형에 의해 반환 sizeof().
chux-복원 Monica Monica

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포인터 유형으로 끝나고 전체 매크로를 무효화합니다 (포인터 매개 변수가있는 함수에서 매크로를 사용하려는 것처럼).

하루가 끝날 때,이 특별한 경우, 결함은 실제로 중요하지 않습니다 (따라서 모든 사람의 시간을 낭비하고 있습니다. huzzah!). 그러나 전 처리기 평가에 대한 요점은 미묘하다고 생각합니다.


2
설명 주셔서 감사합니다. 원래 버전은 컴파일 타임 오류가 발생합니다. Clang은 "첨자 값이 배열, 포인터 또는 벡터가 아닙니다"라고보고합니다. 매크로의 평가 순서에 대한 귀하의 의견은 잘 작성되었지만이 경우에는 바람직한 동작으로 보입니다.
마크 해리슨

1
컴파일러 불만 사항을 잘못된 유형의 자동 알림으로 생각하지 않았습니다. 감사합니다!

3
사용하지 않는 이유가 (sizeof (x) / sizeof (*x))있습니까?
seriousdev

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 ++에서 작동합니다. 차원이 3 이상인 어레이의 경우

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

등, 광고 인피니 엄.


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

유형에 따라 다름 array, 당신은 사용할 필요가없는 한 sizeof(array) / sizeof(array[0])경우 array의 배열 중 하나입니다 char, unsigned 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_element아직 int함께 int 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])은 첫 번째 요소의 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에는 사용 int n = sizeof (a) / sizeof (a[0]);되지 않습니다 (UB). 사용 size_t n = sizeof (a) / sizeof (a[0]);하면이 문제가 발생하지 않습니다.
chux-복원 Monica Monica

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버그가있는 그대로 필요합니다 (4/2/2 기준) :

#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_SIZE하고 size_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()및 일부 GCC 확장 이 필요 합니다 : Expressions의 선언 및 선언 , __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 확장자는 파일 범위에서 사용할 수 없습니다. 파일 범위에서 매크로를 사용하려면 정적 어설 션이 내부에 있어야합니다 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
왜 downvote를 설명해 주시겠습니까? 이것은 sizeof(arr)다른 곳에 표시되지 않은 안전하지 않은 일반적인 구성 ( )에 대한 솔루션을 보여줍니다 ARRAY_BYTES(arr).
Cacahuete Frito

2
ARRAY_SIZE는 자유롭게 사용할 수있을 정도로 일반적이며 ARRAY_BYTES는 이름이 매우 명시 적이며 ARRAY_SIZE 옆에 정의되어야 사용자가 쉽게 볼 수 있고 사용법에 따라 코드를 읽는 사람이 무엇에 대해 의심하지 않는다고 생각합니다 그렇습니다. 내가 의미하는 바는 단순하지 sizeof않고 대신이 구성을 사용하는 것입니다. 매번 이러한 구성을 작성하고 싶을 경우, 실수를하게 될 것입니다 (붙여 넣기하면 매우 일반적이며, 괄호가 많기 때문에 매번 작성하면 매우 일반적 임) ...
Cacahuete Frito

3
..., 그래서 나는 주요 결론에 서 있습니다 : 단일 sizeof은 분명히 안전하지 않으며 (이유는 대답에 있습니다) 매크로를 사용하지 않고 매번 제공 한 구조를 사용하는 것이 더 안전하지 않으므로 갈 수있는 유일한 방법입니다 매크로입니다.
Cacahuete Frito

3
@ MarkHarrison 포인터와 배열의 차이점을 알고 있습니다. 그러나 나중에 작은 함수로 리팩토링 한 함수가 있었으며 처음에는 배열이었고 나중에 포인터였습니다. 크기를 변경하는 것을 잊어 버린 경우 나사를 조이면 볼 수없는 한 지점입니다 그 중 하나.
Cacahuete Frito

3
@hyde 또한 차이점을 알고 있다고해서 모든 사람이 차이점을 알고있는 것은 아니며 기본적으로 이러한 버그를 100 % 제거하는 것을 사용하지 않는 이유는 무엇입니까? 그 버그는 거의 리눅스로 만들었습니다. 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입니다.
Raffi Khatchadourian

문제는 C ++가 아니라 C에 관한 것입니다. STL이 없습니다.
Cacahuete Frito

11
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

6
이것은 배열을 가리키는 포인터가 아닌 실제 배열에서만 작동합니다.
David Schwartz

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-복원 Monica Monica

4

가장 좋은 방법은이 정보를 예를 들어 구조에 저장하는 것입니다.

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

작성, 파괴, 평등 점검 및 기타 필요한 모든 기능을 구현하십시오. 매개 변수로 전달하는 것이 더 쉽습니다.


6
int elements대 이유가 size_t elements무엇입니까?
chux-복원 Monica Monica

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
나는 downvote하지 않았다, 그러나 당신이 옆에 누워 망치를 보지 못했기 때문에 이것은 벽돌로 못을 치는 것과 같습니다. 또한 사람들은 초기화되지 않은 변수를 사용하여 눈살을 찌푸리는 경향이 있습니다 ...하지만 여기서는 그것이 당신의 목적을 충분히 제공한다고 생각합니다.
Dmitri

2
@Dmitri 초기화되지 않은 변수는 여기에 액세스되지 않습니다
MM

1
흠. 포인터 빼기는로 이어집니다 ptrdiff_t. sizeof()결과 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-복원 Monica Monica

1
(char *)(&a+1)-(char *)a는 상수가 아니며 고정 크기로도 런타임에 계산 될 수 있습니다 a[10]. sizeof(a)/sizeof(a[0])이 경우 컴파일 타임에 일정하게 수행됩니다.
chux-복원 Monica Monica

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


0

이미 제공된 답변 외에도 다음을 사용하여 특별한 경우를 지적하고 싶습니다.

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

경우 a하나의 배열입니다 char, unsigned char또는 signed char당신은 사용할 필요가 없습니다 sizeof이후 두 번 sizeof항상에 결과를 않는 이러한 유형의 하나의 피연산자로 표현 1.

C18,6.5.3.4 / 4에서 인용 :

" 경우 sizeof형 갖는 오퍼랜드에인가되는 char, unsigned char또는 signed char(또는 이들의 정규화 된 버전) 결과이다 1."

따라서 if 가 , 또는 의 유형의 배열 인 경우 sizeof(a) / sizeof (a[0])와 같습니다 . 1을 통한 나누기는 중복됩니다.NUMBER OF ARRAY ELEMENTS / 1acharunsigned charsigned char

이 경우 간단히 축약하고 수행 할 수 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.