2013-06-03 3 views
5

길이가 0 인 배열은 가변 길이 구조에 대한 것으로 이해할 수 있습니다. 그러나 우리가 왜 포인터를 사용하지 않는가하는 문제는 같은 방식으로 다른 크기의 구조를 역 참조하고 할당 할 수 있다는 것입니다.포인터 대신 길이가 0 인 배열을 사용하는 이유는 무엇입니까?

편집 - 가정 의견

에서 추가 예 :

struct p 
{ 
    char ch; 
    int *arr; 
}; 

우리는 이것을 사용할 수 있습니다 :

struct p *p = malloc(sizeof(*p) + (sizeof(int) * n)); 

p->arr = (struct p*)(p + 1); 

메모리의 연속 된 덩어리를 얻을 수 있습니다. 그러나 나는 공간이 p->arr 인 것을 잊어 버린 것처럼 보이고 제로 사이즈 배열 방법과는 다른 것으로 보인다.

+2

예를 제공해주십시오. – Arafangion

+1

구조체에 관한 설명. C90에서는'lastMemberOfArray []', C99에서는'lastMemberOfArray []', 비표준 GNU에서는'lastMemberOfArray [0]'의 3 가지 경우가 있습니다. C90의 경우 이것은 정의되지 않은 동작에 의존하는 더러운 해킹입니다. 구조체 끝에 패딩 바이트 구조체가 있으면 문제가 발생할 수 있습니다. C99에서는이를 수정하고 _flexible array member_라는 유형을 만들었는데, 이는 동일한 방식으로 작동하지만 잘 정의 된 동작을합니다. 마지막으로, 비표준 GNU는 동일한 목적으로 zero-size 배열을 허용합니다. '-std = c99 -pedantic-errors'를 표준으로 컴파일하면'[0]'은 컴파일되지 않습니다. – Lundin

+1

@ Lundin의 첫 번째 예제는 "lastMemberOfArray [1]'in C90"이어야합니다. 단순한 오타가 기억이 나지 않는 사람들을위한 것입니다. –

답변

5

이들은 다양한 유형의 소위 "struct hack"이며, comp.lang.c FAQ의 질문 2.6에서 논의되었습니다.

크기가 0 인 배열을 정의하는 것은 사실 C에서는 불법이며 적어도 1989 ANSI 표준 이후였습니다. 일부 컴파일러에서는이를 확장으로 허용하지만,이 옵션을 사용하면 이식 가능하지 않은 코드가 생깁니다.

이 구현하는 더 휴대용 방법은 예를 들어, 길이 1의 배열을 사용하는 것입니다

struct foo { 
    size_t len; 
    char str[1]; 
}; 

당신은 할당 된 크기를 추적 할 len를 사용하여 이상 sizeof (struct foo) 바이트를 할당하고 수 str[N]에 액세스하여 배열의 N 번째 요소를 가져옵니다. C 컴파일러는 일반적으로 배열 범위 검사를 수행하지 않으므로 일반적으로 "작동"합니다. 그러나 엄밀히 말하면 동작은 정의되지 않습니다.

1999 ISO 표준이 사용 대체하기위한 "유연한 배열 구성원"기능 추가 :

struct foo { 
    size_t len; 
    char str[]; 
}; 

당신은 이전 구조체의 해킹과 같은 방법으로이 다룰 수 있지만 동작은 잘 정의 된. 그러나 당신은 모든 부기를 스스로해야합니다. sizeof (struct foo)에는 여전히 배열의 크기가 포함되지 않습니다.

struct bar { 
    size_t len; 
    char *ptr; 
}; 

을 그리고 이것은 완벽하게 좋은 방법이지만 다른 의미가 있습니다

당신은, 물론, 대신 포인터를 사용할 수 있습니다. "struct hack"이나 유연한 배열 멤버의 가장 큰 장점은 배열이 나머지 구조체와 연속적으로 할당된다는 것입니다. 배열이 memcpy을 사용하여 구조체와 함께 복사 할 수 있습니다 (대상이 적절하게 할당 됨). 포인터를 사용하면 배열이 별도로 할당됩니다. 원하는 것과 정확히 같을 수도 있고 그렇지 않을 수도 있습니다.

8

포인터가 실제로 필요하지 않으므로 아무런 이익이없는 공간이 필요합니다. 또한 간접 지정은 다른 수준의 간접 지정을 의미 할 수 있으며 실제로 필요하지도 않습니다.

동적 정수 배열이 선언의 예를 비교해

typedef struct { 
    size_t length; 
    int data[0]; 
} IntArray1; 

하고 :

typedef struct { 
    size_t length; 
    int *data; 
} IntArray2; 

기본적 포인터 "표현 어레이의 첫 번째 요소는 수,이 주소 인 일반적으로 필요한 것보다 더 일반적인 것 "이되어야합니다. 원하는 모델은 "배열의 첫 번째 요소가 바로 여기에 있지만 배열의 크기가 얼마나 큰지 알지 못합니다."입니다.

물론 두 번째 형식을 사용하면 "기본"주소 (IntArray2 구조체의 주소)가 변경 될 수 있으므로 실제로 배열을 확장 할 수 있습니다. 기본 구조와 정수 데이터 요소를 함께 할당해야하므로 IntArray1으로는이 작업을 수행 할 수 없습니다. 트레이드 오프, 트레이드 오프 ...

+0

배열의 크기가 0 일 수 없음 C11 6.7.6.2 – Lundin

12

포인터를 사용하면 구조는 더 이상 가변 길이가 아니며 길이는 고정되지만 데이터는 다른 위치에 저장됩니다.

길이가 0 인 배열 뒤에있는 아이디어 *은 구조의 나머지 데이터와 함께 배열의 데이터를 "줄에"저장하여 배열의 데이터가 메모리의 구조체 데이터를 따르도록하기위한 것입니다. 별도로 할당 된 메모리 영역을 가리키는 포인터는 그렇게 할 수 없습니다.


* 이러한 배열은 플렉시블 어레이를라고도; C99에서 element_type flexArray[0] 대신 element_type flexArray[]으로 표시합니다. 즉, 0을 버립니다.

1

포인터를 사용하면 별도의 할당과 할당이 필요하기 때문입니다.

struct WithPointer* withPointer = malloc(sizeof(struct WithPointer)); 
withPointer.array = malloc(ARRAY_SIZE * sizeof(int)); 

이 WithArray의 '목적'을 얻으려면 : 그것 뿐이다

struct WithArray* withArray = malloc(sizeof(struct WithArray) + 
              (ARRAY_SIZE - 1) * sizeof(int)); 

struct WithPointer 
{ 
    int someOtherField; 
    ... 
    int* array; 
}; 

struct WithArray 
{ 
    int someOtherField; 
    ... 
    int array[1]; 
}; 

은 당신이해야 할 WithPointer의 '객체'를 얻으려면.

어레이를 연속 메모리로 유지하는 것이 매우 편리하거나 필요한 경우도 있습니다. 예를 들어 네트워크 프로토콜 패킷에서.

+2

배열의 크기가 0 일 수 없음 C11 6.7.6.2 – Lundin

+0

@ Lundin 당신 말이 맞아요, 위의 코드를 수정했습니다. 고마워요. –

관련 문제