2009-08-05 1 views
1

별도의 라이브러리에서 구조체는 다음과 같습니다.구조체 내의 공유 객체 : 호출 프로그램과 라이브러리 사이 (c)

typedef struct bsmat{ 
int m; 
int *n; 
double **d; 
} bs; 

여기서 ** d는 double 배열에 대한 포인터의 배열입니다.

bs *new_bs(int n, double **d); 

두 가지 사용 사례가 있습니다 :

(a) 주 응용 프로그램은 여러 개의 double 행렬을 할당하고 라이브러리를 호출하여 구조를 만듭니다.

b = new_bs(5, p) 

(b) 또는 함수를 호출하여 라이브러리에서 객체를 생성 할 수 있습니다.

bs *add(bs *a, bs *b); 

두 번째 경우에는 라이브러리가 ** d를 소유하고 있습니다. 필요할 때 해방하십시오. 첫 번째 경우에는 응용 프로그램에서 배열을 할당하고 라이브러리에서 읽기/쓰기가 가능합니다. 누가 언제 무엇을 자유롭게 해야하는지에 관해서 나에게 명확하지 않다? ??

기본 응용 프로그램이 struct-member를 소유하도록 허용하는 것이 문제입니까? 이 접근 방식의 문제점은 무엇입니까? 대안은 무엇입니까? 많은 수의 배열을 앞뒤로 복사하는 것을 피하려고합니다.

덕분

TR

답변

3

, 그것은 중요 그 라이브러리 기록한 '계약'이 자사의 서비스를 사용하는 응용 프로그램에 있습니다. 라이브러리 작성자는 라이브러리가 만든 모든 할당과 응용 프로그램에서 해제해야하는 것을 문서화합니다. 다른 계약과 마찬가지로 간단하고 일관성이있는 것이 좋습니다. 이 특별한 경우

, 나는 도서관도 제공해야한다고 가정 구조를 자유롭게

void free_bs(bs *b) 
{ 
    if (b->d) { 
      /* free b->d here */ 
    } 
    /* free rest of the structure */ 
} 

합니다. 여기서 d 배열을 해제하는 것도 의미가 있습니다. 응용 프로그램에는 모든 bs 구조에 대한 포인터가 있으며 더 이상 필요하지 않을 때 free_bs을 호출 할 책임이 있습니다.

예를 들어 향후 사용을 위해 d를 유지하려면 라이브러리 작업을 완료 한 후에 계약서가 좀 더 복잡합니다. 이 라이브러리는 또한 제공 할 수있다 : d에 대한 포인터를 반환하고 무료 루틴을 건너 뛸 알 수 있도록 필드를 비워 둡니다

double** bs_get_data(bs *b) 
{ 
    double **d = b->d; 
    b->d = NULL; 
    return b->d; 
} 

합니다.

bs_get_data()의 문서는 한 번만 호출 할 수 있으며 응용 프로그램에서는이 경우 배열을 해제 할 책임이 있음을 설명해야합니다.

UPDATE : 아래의 코멘트에 대한 답변에서 : 우선은, 다음 (적어도) 나는 가정하여 문제를 단순화 것을 참고 : d이 하나의 bs 구조 또는 응용 프로그램 중 하나를 참조 . 응용 프로그램과 라이브러리는 배열에 대한 하나의 참조를 서로 "전달"합니다.동일한 d 배열을 bs 구조체에 넣고 싶다면 내 접근 방식으로는 충분하지 않습니다.

의견에 제안한 플래그는 도움이 될 수 있지만 표준 방식 인 FWIK는 아닙니다. 이 경우, 간단하게 참조 계산을 구현하는 것이 좋습니다. new_bs()에서 1.

typedef struct { 
     double **d; 
     int ref; 
} d_t; 

초기화 refd의 "복사"를받는 모든 기능에, 참조 카운트를 증가 : d 주위를 공유 할 필요가있는 자원 인 경우에게 "개체"를 만들 . bs 구조가 삭제되거나 응용 프로그램에 d이 더 이상 필요하지 않은 경우 참조 횟수를 감소시킵니다. 0이되면 해제하십시오. 어떤면에서는 고급 언어가 당신을 위해하는 일이며 자원 관리를 제재로 유지하는 데 매우 효율적입니다.

아무도 그 어레이를 소유하고 있지 않지만, 누구도 그 어레이를 소유하고 있지 않습니다.

물론 작업이 많이 필요하고 코드가 복잡하므로 균형을 맞추어보세요. 소유하고있는 모든 구조에는 필요하지 않지만 여러 참조를 유지해야하는 경우에만 필요합니다.

+0

매우 빠른 답변을 보내 주셔서 감사합니다. ** d의 소유권을 표시하는 "storageMode"(내부/외부) 필드를 포함시키는 것이 합리적입니까? free_bs는 내부적으로 소유 된 경우에만 해제됩니다 (b -> d). 이렇게하면 호출자가 ** d를 해제해야 할 책임이 있다는 점에서 계약이 명시 적으로 적용됩니까? 구조체 내에 공유 객체를 사용하는 데 다른 문제가 있습니까? OO 사람들이 그런 연습을 싫어하는 것 같습니다! 거기에 합리적인 대안, 데이터 복사를 포함하지 ?? 감사합니다. tr – user151410

+0

응답에서 내 대답을 편집 했으므로 더 쓸 수 있습니다 :-). 즉, 참조 카운팅을 고려할 수 있습니다. – tsg

1

이 문제는 참조 횟수가 과도하다고 생각합니다. 하지만 인터페이스가 *b이고 누가 b->d이 아닌지 더 정확하게 지정해야합니다. 도서관이 "필요할 때"(당신이 넣었을 때) 언제 b->d을 무료로 알 수 있는지는 내게는 분명하지 않습니다. 다음과 같이

나는 인터페이스를 확장하는 것 : 하나 외부 관리 또는 내부 관리

  1. 유형 bs *의 모든 포인터이다. 함수 new_bs은 외부 관리되는 포인터를 반환합니다. 함수 add은 내부적으로 관리되는 것을 반환합니다.

  2. 모든bs * 유형의 포인터를 반환하는 함수는 결과 관리가 내부인지 외부인지를 말해야합니다.

  3. 당신은 아마
    void bs_free_ext(bs **); // free *bs and set *bs = NULL (external mgmt) 
    void bs_free_int(bs **); // free *bs and set *bs = NULL (internal mgmt) 
    

    bs_free_ext*bs 해제 기능

    을 제공한다; bs_free_int*bs(*bs)->d을 모두 해제합니다.

  4. 안전을 위해 각 구조체의 저장소 관리를 기록하는 필드를 추가하고 할당하거나 해제하는 함수에 대한 어설 션을 사용합니다. (이유 : 주장 검사는 할당 또는 해제의 비용에 비해 저렴합니다.)

리눅스에 응용 프로그램을 실행할 수있는 경우 valgrind에 확인 후 메모리가 플러스입니다.

P. 거의 모든 인터페이스에 메모리 관리 정보가 누설된다는 사실은 C로 프로그래밍하는 이점을 얻기 위해 지불하는 비용 중 하나입니다.

+0

안녕하세요, Norman, 답변 해 주셔서 감사합니다. 내 모든 작은 물체가 응용 프로그램이나 도서관에 의해 동시에 만들어지고 살해 된 것 같습니다. 그래서 구조체에 storage_mode (internal/external) 필드를 추가하는 것은 곧장 앞으로의 해결책처럼 보입니다. 이 솔루션 자체는 Thoriann이 제안한대로 도서관 사용자가 계약에 동의하도록하여 안전 메커니즘을 추가합니다. 응용 프로그램에서 라이브러리가 b -> d를 할당 할 수있는 인스턴스가 있습니다. 따라서 별도의 생성자 bs_new_ext & bs_new_int를 사용하거나 storage_mode를 매개 변수로 전달하는 것이 좋습니다. 둘 다에게 감사드립니다. – user151410

+0

나는 나의 이전 코멘트에서 정확하게 600자를 사용했다 !! C를 처음 접하기 때문에 동적 메모리 관리가 제대로 이루어지지 않고 응용 프로그램과 라이브러리 간의 메모리 위치가 비현실적으로 공유 될 수 있습니다. bs의 수명 동안 걱정해야 할 다른 것들이 있습니까? 두 당사자가 우연히 서로의 발가락을 밟는 것에 대해 b-> d가 있습니까? 나는 앱이 앱의 요청에 따라 BS를 계산에 사용하도록 B-> d와 라이브러리를 업데이트 할 것으로 기대한다. 그러나 비 합의 데이터 교환이 발생할 수있는 예기치 않은 방법이 있다고 확신합니다. 어떤 안전 장치가 그것을 막는가? – user151410