programing

C에서 플렉시블 어레이 멤버를 사용하는 것은 나쁜 방법입니까?

randomtip 2022. 8. 10. 19:48
반응형

C에서 플렉시블 어레이 멤버를 사용하는 것은 나쁜 방법입니까?

최근에 C에서 플렉시블 어레이 멤버를 사용하는 것은 소프트웨어 엔지니어링의 잘못된 관행이라는 것을 알게 되었습니다.그러나, 그 진술은 어떠한 주장도 뒷받침되지 않았다.이것은 인정된 사실입니까?

(플렉시블 어레이 멤버는 C99에서 도입된 C기능으로, 마지막 요소가 지정되지 않은 크기의 어레이임을 선언할 수 있습니다.예: )

struct header {
    size_t len;
    unsigned char data[];
};

아니요, C에서 유연한 어레이 멤버를 사용하는 것도 나쁘지 않습니다.

이 언어 기능은 ISO C99, 6.7.2.1(16)에서 최초로 표준화되었습니다.다음 개정판 ISO C11에서는 섹션 6.7.2.1(18)에 명시되어 있습니다.

다음과 같이 사용할 수 있습니다.

struct Header {
    size_t d;
    long v[];
};
typedef struct Header Header;
size_t n = 123; // can dynamically change during program execution
// ...
Header *h = malloc(sizeof(Header) + sizeof(long[n]));
h->n = n;

또는 다음과 같이 할당할 수 있습니다.

Header *h = malloc(sizeof *h + n * sizeof h->v[0]);

주의:sizeof(Header)에는 최종 패딩 바이트가 포함되어 있기 때문에 다음 할당이 잘못되어 버퍼 오버플로우가 발생할 수 있습니다.

Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!

유연한 어레이 멤버를 가진 구조체는 할당 수를 1/2 줄일 수 있습니다.즉, 1개의 구조 오브젝트에 2개의 할당이 아니라 1개만 있으면 됩니다.즉, 메모리 할당자의 부기 오버헤드에 의해 소비되는 수고를 줄이고 메모리를 줄입니다.또한 하나의 포인터를 추가하기 위해 스토리지를 저장합니다.따라서 이러한 구조 인스턴스를 대량으로 할당해야 하는 경우 프로그램의 실행 시간과 메모리 사용률이 (상수 계수에 따라) 현저하게 향상됩니다.

이와는 대조적으로 정의되지 않은 동작을 일으키는 유연한 어레이 멤버에 대해 표준화되지 않은 구조를 사용합니다(예:long v[0];또는long v[1];)는 분명히 나쁜 관행입니다.따라서 정의되지 않은 동작과 마찬가지로 이를 피해야 합니다.

ISO C99는 20여 년 전인 1999년에 출시되었기 때문에 ISO C89 호환성에 대한 노력은 설득력이 없습니다.

goto를 사용하는 것은 소프트웨어 엔지니어링의 잘못된 관행이라는 것은 인정되고 있는 사실이다.그건 사실이 아니야.goto가 도움이 되는 경우가 있습니다.특히 청소 처리나 어셈블러에서 포팅할 때 편리합니다.

플렉시블 어레이 구성원은 RiscOS의 윈도 템플릿 포맷과 같은 레거시 데이터 포맷을 매핑하는 것을 머릿속에서 쉽게 떠올릴 수 있습니다.약 15년 전만 해도 매우 유용했을 것입니다.그리고 아직도 그런 것들을 다루는 사람들이 있을 것입니다.

플렉시블 어레이 멤버를 사용하는 것이 좋지 않은 방법이라면 모두 C99 사양의 작성자에게 알려 주십시오.나는 그들이 다른 답을 가지고 있을지 의심스럽다.

그러니까...

struct header
{
 size_t len;
 unsigned char data[];
}; 

C에서는 그것이 일반적인 관용어입니다.많은 컴파일러가 다음을 수용한다고 생각합니다.

  unsigned char data[0];

네, 위험합니다만, 일반 C 어레이보다 위험하지는 않습니다.즉, 매우 위험합니다.-).알 수 없는 크기의 어레이가 정말로 필요한 경우에만 주의하여 사용하십시오.다음과 같은 방법으로 메모리의 malloc 및 해방 상태를 확인합니다.

  foo = malloc(sizeof(header) + N * sizeof(data[0]));
  foo->len = N;

다른 방법은 데이터를 요소에 대한 포인터로 만드는 것입니다.그런 다음 필요에 따라 데이터를 올바른 크기로 재할당()할 수 있습니다.

  struct header
    {
     size_t len;
     unsigned char *data;
    }; 

물론 C++에 대해 묻는다면 이 두 가지 모두 좋지 않은 방법입니다.그 대신 STL 벡터를 사용합니다.

참고로 C89 호환성을 위해 이러한 구조는 다음과 같이 할당해야 합니다.

struct header *my_header
  = malloc(offsetof(struct header, data) + n * sizeof my_header->data);

또는 매크로를 사용한 경우:

#define FLEXIBLE_SIZE SIZE_MAX /* or whatever maximum length for an array */
#define SIZEOF_FLEXIBLE(type, member, length) \
  ( offsetof(type, member) + (length) * sizeof ((type *)0)->member[0] )

struct header {
  size_t len;
  unsigned char data[FLEXIBLE_SIZE];
};

...

size_t n = 123;
struct header *my_header = malloc(SIZEOF_FLEXIBLE(struct header, data, n));

유연한 설정_SIZE_MAX에 대한 SIZE는 실패가 거의 확실합니다.

struct header *my_header = malloc(sizeof *my_header);

구조물이 사용되는 방법에는 단점이 몇 가지 있는데, 그 의미를 잘 생각하지 않으면 위험할 수 있습니다.

예를 들어, 함수를 시작하는 경우:

void test(void) {
  struct header;
  char *p = &header.data[0];

  ...
}

그러면 데이터에 할당된 스토리지가 없기 때문에 결과가 정의되지 않습니다.이것은 통상적으로 알고 있는 것이지만, C프로그래머가 구조체의 값 의미론을 사용하는 것에 익숙해져 있는 경우가 있습니다.이것은 다양한 방법으로 분류됩니다.

예를 들어 다음과 같이 정의합니다.

struct header2 {
  int len;
  char data[MAXLEN]; /* MAXLEN some appropriately large number */
}

그러면 단순히 할당으로 두 개의 인스턴스를 복사할 수 있습니다.

struct header2 inst1 = inst2;

또는 포인터로 정의된 경우:

struct header2 *inst1 = *inst2;

그러나 플렉시블 어레이 멤버의 컨텐츠는 복사되지 않기 때문에 이 방법은 사용할 수 없습니다.필요한 것은 구조 크기를 동적으로 malloc하여 어레이 위에 복사하는 것입니다.memcpy또는 이에 상당합니다.

struct header3 {
  int len;
  char data[]; /* flexible array member */
}

마찬가지로, A를 수용하는 함수의 쓰기struct header3함수 호출의 인수는 다시 값으로 복사되므로 얻을 수 있는 것은 플렉시블 어레이 멤버의 첫 번째 요소뿐이기 때문에 동작하지 않습니다.

 void not_good ( struct header3 ) ;

이 방법을 사용하는 것이 나쁜 생각은 아니지만 이러한 구조를 항상 동적으로 할당하고 포인터로 전달할 필요가 있습니다.

 void good ( struct header3 * ) ;

이 답변 아래의 코멘트를 주의 깊게 읽어 주십시오.

C 표준화가 진행됨에 따라 [1]을(를) 더 이상 사용할 필요가 없습니다.

이 기능을 사용하지 않는 이유는 이 기능을 사용하기 위해 코드를 C99에 연결하는 것은 가치가 없기 때문입니다.

요점은 다음과 같은 관용어를 항상 사용할 수 있다는 것입니다.

struct header {
  size_t len;
  unsigned char data[1];
};

그것은 완전히 휴대할 수 있습니다.어레이 내의 n개의 요소에 메모리를 할당할 때 1을 고려할 수 있습니다.data:

ptr = malloc(sizeof(struct header) + (n-1));

다른 이유로 코드를 빌드하기 위한 요건으로 C99를 이미 가지고 있거나 특정 컴파일러를 대상으로 하고 있는 경우에는 아무런 피해가 없습니다.

C 인터페이스와 구현에서 본 적이 있습니다.

  struct header {
    size_t len;
    unsigned char *data;
};

   struct header *p;
   p = malloc(sizeof(*p) + len + 1 );
   p->data = (unsigned char*) (p + 1 );  // memory after p is mine! 

참고: 데이터는 마지막 멤버일 필요는 없습니다.

언급URL : https://stackoverflow.com/questions/246977/is-using-flexible-array-members-in-c-bad-practice

반응형