2015-02-03 11 views
1

이 질문은 실제로 Python/C API (PyObject_NewVar, PyObject_VAR_HEAD, PyTypeObject.tp_basicsize and .tp_itemsize에 가변 길이 유형을 사용하는 방법에 관한 것이지만이 질문은 귀찮게하지 않고도 물어볼 수 있습니다. API의 세부 사항을 알고 싶습니다. struct 안에 배열을 사용해야한다고 가정하십시오.동적으로 할당 된 구조체 (배열 구조체)에 동적 배열 할당

두 가지 방법 중 하나로 목록 데이터 구조를 만들 수 있습니다 (지금은 char 목록 만 보겠다. 중요하지 않습니다.) 첫 번째 포인터는 두 개의 할당이 필요합니다. #include 무시하고 오류 처리 :

struct listptr { 
    size_t elems; 
    char *data; 
}; 
struct listptr *listptr_new(size_t elems) { 
    size_t basicsize = sizeof(struct listptr), itemsize = sizeof(char); 
    struct listptr *lp; 
    lp = malloc(basicsize); 
    lp->elems = elems; 
    lp->data = malloc(elems * itemsize); 
    return lp; 
} 

목록을 만드는 두 번째 방법은 배열 표기법과 하나의 할당을 사용합니다. (나는 꽤 그것을 철저하게 테스트했기 때문에이 두 번째 구현이 작동 알고있다.) 두 경우 모두

struct listarray { 
    size_t elems; 
    char data[1]; 
}; 
struct listarray *listarray_new(size_t elems) { 
    size_t basicsize = offsetof(struct listarray, data), itemsize = sizeof(char); 
    struct listarray *la; 
    la = malloc(basicsize + elems * itemsize); 
    la->elems = elems; 
    return lp; 
} 

, 당신이 다음 배열에 액세스 할 수 lp->data[index]를 사용합니다.

제 질문은 왜 두 번째 방법이 효과가 있습니까? char data[], char data[0], char *data 또는 char data 대신 char data[1]을 신고하는 이유는 무엇입니까? 특히, 의 작업 방식에 대한 직관적 인 이해는 data을 선언하는 올바른 방법은 포인터 또는 배열 표기법이 전혀없는 char data이라는 것입니다. 마지막으로, basicsizeitemsize의 두 계산에서 모두 올바른 계산입니다? 특히, 모든 컴퓨터에서 올바른 offsetof 사용이 보장됩니까?

업데이트

분명히이 호출되는 struct hack : C99에서, 당신은 flexible array member 사용할 수 있습니다 이해와

struct listarray2 { 
    size_t elems; 
    char data[]; 
} 

을 당신 런타임에 data에 대한 malloc 충분한 공간이 있습니다. C99 이전에는 선언이 일반적이었습니다. 그래서 지금 내 질문은 char data[1] 또는 대신 char *data 또는 char data을 선언하는 이유는 무엇입니까?

답변

1

이유는 char data[1] 또는 char data[] 대신 char *data 또는 char data직접 직렬화 및 deserializable 당신의 구조를 유지하는 것입니다 선언하는 것입니다. 이러한 종류의 구조를 디스크 나 네트워크 소켓 등을 통해 작성하는 경우 중요합니다.

예를 들어 두 개의 할당이 필요한 첫 번째 코드 스 니펫을 예로들 수 있습니다. listptr 유형은 직접 직렬화 할 수 없습니다. 즉 listptr.elems와 listptr.data가 가리키는 데이터는 인접한 메모리에 없습니다. 일반 구조로 디스크에서 /로이 구조를 읽고 쓰는 방법은 없습니다. 이를 수행하려면 struct listptr 유형의 사용자 정의 함수가 필요합니다. 즉, 직렬화하면 먼저 elems을 디스크에 쓰고 데이터 포인터가 가리키는 데이터를 작성해야합니다. deserialization에서 elems를 읽고, listptr.data에 적절한 공간을 할당 한 다음 디스크에서 데이터를 읽어야합니다.

유연한 배열 구성원을 사용하면 listptr.elem과 listptr.data가 인접한 메모리 공간에 있기 때문에이 문제를 해결할 수 있습니다. 그래서 그것을 직렬화하기 위해서 구조체에 할당 된 전체 크기와 구조체 자체를 간단하게 쓸 수 있습니다. deserialize 할 때 먼저 할당 된 크기를 읽고 필요한 공간을 할당 한 다음 listptr 구조체를 해당 공간으로 읽습니다.

정말 필요한 이유가 궁금 할 지 모르지만 귀중한 기능입니다. 이기종 유형의 데이터 스트림을 고려하십시오. 이질적 유형과 크기를 정의하는 헤더를 정의하고 스트림의 각 유형 앞에이 헤더를두면 데이터 스트림을 일반적으로 매우 우아하고 효율적으로 직렬화 및 비 직렬화 할 수 있습니다.

내가 보다 char data[1]을 선택하는 유일한 이유는 C++에 유연한 배열 구성원에 대한 지원이 없으므로 C99와 C++간에 이식성이 필요한 API를 정의하는 경우입니다. 당신이 char data 대신 char data[1] 또는 char data[]을 사용하지 왜 당신은 또한 요청

size_t totalsize = offsetof(struct listarray, data[elems]); 

:

는 또한, char data[1]에 다음과 같은 얻을 수있는 총 필요한 구조의 크기를 할 수 있다고 지적하고 싶었다. 엄밀히 말하면 평범한 구식 인 char data을 기술적으로 사용하는 것이 가능하지만, 도덕적으로는 (IMHO) 기피 될 것입니다.

  1. 당신은 문자의 배열을 원했지만 지금은 배열로 직접 data 멤버에 액세스 할 수 없습니다 :이 방법의 두 가지 문제가 있습니다. 어레이로 액세스하려면 주소 data의 포인터를 가리켜 야합니다. 즉

    char * as_array = & listarray.data;

  2. 구조 정의 (및 코드의 구조 사용)는 코드를 읽는 모든 사람을 오해 할 수 있습니다. 실제로 char 배열을 의미 할 때 하나의 char을 선언하는 이유는 무엇입니까? 사람이 char data[1]에 찬성 char data을 사용하는 이유를 다음 두 가지 감안할 때

, 나도 몰라. 대안을 가진 사람에게는 이익이되지 않습니다.

+0

'char data [1]'과'char data []'는 구조체와 연속 된 배열을 유지한다는 것을 알고 있습니다. 내 질문에 묻는 질문은 왜'char data'가 두 번째 코드 예제와 같은 방식으로 작동하지 않는가하는 것입니다. – wkschwartz

+0

@wkschwartz 기술적으로는 효과가 있지만 왜 내 생각에 대한 내 대답이 업데이트되는지 확인하십시오. – mshildt