C / C ++에서 0 크기 배열을 정의하면 어떻게됩니까? 나는 내가이 핸들 케이스에있는 경우 알아

궁금한 점 int array[0];은 코드에서 길이가 0 인 배열 을 정의하면 실제로 어떻게됩니까 ? GCC는 전혀 불평하지 않습니다.

샘플 프로그램

#include <stdio.h>

int main() {
    int arr[0];
    return 0;
}

설명

실제로 Darhazer의 의견에서 가변 길이처럼 지적되는 대신 길이가 0 인 배열이 이런 식으로 초기화되었는지 알아 내려고 노력하고 있습니다.

나는 야생으로 몇 가지 코드를 공개해야하기 때문에 나는 내가이 핸들 케이스에있는 경우 알아 내기 위해 노력하고, 그래서 이것은이다 SIZE로 정의 0정적으로 정의와 일부 코드에서 발생,int array[SIZE];

나는 실제로 GCC가 불평하지 않아서 내 질문에 이르렀다는 것에 놀랐다. 내가받은 답변에서 경고의 부족은 주로 새로운 [] 구문으로 업데이트되지 않은 오래된 코드를 지원하기 때문이라고 생각합니다.

나는 주로 오류에 대해 궁금해했기 때문에 Lundin의 대답을 올바른 것으로 태그하고 있습니다 (Nawaz는 처음이지만 완벽하지는 않았습니다). 내가 원하는 것을 정확하게.



답변

배열의 크기는 0 일 수 없습니다.

ISO 9899 : 2011 6.7.6.2 :

표현식이 상수 표현식 인 경우 0보다 큰 값을 가져야합니다.

위의 텍스트는 일반 배열 (1 항) 모두에 해당됩니다. VLA (가변 길이 배열)의 경우 표현식 값이 0보다 작거나 같으면 동작이 정의되지 않습니다 (문단 5). 이것은 C 표준의 규범적인 텍스트입니다. 컴파일러는 다르게 구현할 수 없습니다.

gcc -std=c99 -pedantic VLA가 아닌 경우에 대한 경고를 제공합니다.


답변

표준에 따라 허용되지 않습니다.

그러나 C 컴파일러에서 이러한 선언을 FAM (Flexible Array Member ) 선언 으로 취급하는 것이 현재 관행입니다 .

C99 6.7.2.1, §16 : 특별한 경우로, 하나 이상의 명명 된 멤버가있는 구조의 마지막 요소는 불완전한 배열 유형을 가질 수 있습니다. 이것을 유연한 배열 구성원이라고합니다.

FAM의 표준 구문은 다음과 같습니다.

struct Array {
  size_t size;
  int content[];
};

아이디어는 다음과 같이 할당한다는 것입니다.

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

정적으로 사용할 수도 있습니다 (gcc 확장명).

Array a = { 3, { 1, 2, 3 } };

이것을 테일 패딩 구조 (이 용어는 C99 표준의 발행 이전) 또는 구조체 해킹 (Joe Wreschnig가 지적한 덕분)으로도 알려져 있습니다.

그러나이 구문은 최근 C99에서만 표준화 (및 효과 보장)되었습니다. 일정한 크기가 필요하기 전에.

  • 1 다소 이상했지만 휴대용 방법이었습니다.
  • 0 의도를 나타내는 데 더 좋았지 만 표준이 gcc를 포함한 일부 컴파일러의 확장으로 관련되고 지원되는 한 합법적이지는 않았습니다.

그러나 테일 패딩 실습은 스토리지를 사용할 수 있다는 점 (주의 malloc) 에 의존 하므로 일반적으로 스택 사용량에 적합하지 않습니다 .


답변

표준 C 및 C ++에서는 크기가 0 인 배열이 허용 되지 않습니다.

GCC를 사용하는 경우 -pedantic옵션으로 컴파일하십시오 . 다음과 같이 경고합니다 .

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

C ++의 경우 비슷한 경고가 표시됩니다.


답변

그것은 완전히 불법이며 항상 그렇습니다. 그러나 많은 컴파일러는 오류를 알리지 않습니다. 왜 당신이 이것을하고 싶은지 모르겠습니다. 내가 아는 한 가지 사용법은 부울에서 컴파일 시간 오류를 발생시키는 것입니다.

char someCondition[ condition ];

경우 condition거짓이다, 나는 컴파일 타임 오류가 발생합니다. 그러나 컴파일러는 이것을 허용하기 때문에 다음을 사용했습니다.

char someCondition[ 2 * condition - 1 ];

이것은 1 또는 -1의 크기를 제공하며, -1의 크기를 허용하는 컴파일러를 찾지 못했습니다.


답변

이 주장에 gcc 온라인 문서의 전체 페이지 가 있다고 덧붙입니다.

인용문 :

GNU C에서는 길이가 0 인 배열이 허용됩니다.

ISO C90에서는 내용의 길이를 1로 지정해야합니다

3.0 이전의 GCC 버전에서는 길이가 0 인 배열을 마치 유연한 배열 인 것처럼 정적으로 초기화 할 수있었습니다. 유용한 경우 외에도 나중에 데이터를 손상시키는 상황에서 초기화를 허용했습니다.

그래서 당신은 할 수

int arr[0] = { 1 };

그리고 붐 🙂


답변

길이가 0 인 배열의 또 다른 용도는 가변 길이 객체 (pre-C99)를 만드는 것입니다. 제로 길이 배열이 되어 서로 다른가요 배열 [] 0 않고있다.

gcc doc 에서 인용 :

길이가 0 인 배열은 GNU C에서 허용됩니다. 실제로 가변 길이 객체의 헤더 인 구조의 마지막 요소로 매우 유용합니다.

 struct line {
   int length;
   char contents[0];
 };

 struct line *thisline = (struct line *)
   malloc (sizeof (struct line) + this_length);
 thisline->length = this_length;

ISO C99에서는 구문과 의미가 약간 다른 유연한 배열 멤버를 사용합니다.

  • 유연한 배열 멤버는 0없이 contents []로 작성됩니다.
  • 유연한 배열 구성원의 유형이 불완전하므로 sizeof 연산자가 적용되지 않을 수 있습니다.

실제 예는의 길이 제로의 배열 인 struct kdbus_itemkdbus.h (리눅스 커널 모듈).


답변

구조체 내에서 크기가 0 인 배열 선언은 허용되는 경우 유용하고 의미론이 (1) 정렬을 강제하지만 공간을 할당하지 않고 (2) 배열을 인덱싱하는 것은 정의 된 동작으로 간주됩니다. 결과 포인터가 구조체와 동일한 메모리 블록 내에있는 경우 이러한 동작은 어떤 C 표준에서도 허용되지 않았지만 일부 구형 컴파일러는 빈 대괄호로 불완전한 배열 선언을 허용하는 컴파일러의 표준이되기 전에 허용했습니다.

1 크기의 배열을 사용하여 일반적으로 구현되는 구조체 해킹은 복잡하며 컴파일러가 그것을 깨뜨리는 것을 요구하지 않는다고 생각합니다. 예를 들어, 컴파일러가을 볼 경우으로 간주 int a[1]할 수있는 권한이 있다고 생각 a[i]합니다 a[0]. 누군가가 다음과 같은 것을 통해 구조체 해킹의 정렬 문제를 해결하려고하면

typedef 구조체 {
  uint32_t 크기;
  uint8_t 데이터 [4]; // 패딩이 구조체의 크기를 버리지 않도록 4를 사용하십시오.
}

컴파일러는 영리하고 배열 크기가 실제로 4라고 가정합니다.

; 쓰여진대로
  foo = myStruct-> 데이터 [i];
; 해석 된대로 (little-endian 하드웨어 가정)
  foo = (((* (uint32_t *) myStruct-> data) >> (i << 3)) & 0xFF;

이러한 최적화는 특히 myStruct->data와 동일한 작업에서 레지스터에로드 될 수있는 경우에 합리적 일 수 있습니다 myStruct->size. 나는 표준에서 그러한 최적화를 금지하는 것은 아무것도 모르지만 물론 네 번째 요소를 넘어서서 액세스 할 수있는 코드를 깨뜨릴 것입니다.