C의 "실제"크기 조정 변수는 얼마나 유용합니까?


9

항상 직관적으로 C의 긍정적 인 기능 (gcc, clang 등의 구현 중 하나)으로 나를 직관적으로 강타 한 가지는 런타임에 자신의 변수 옆에 숨겨진 정보를 저장하지 않는다는 사실입니다. 이것은 예를 들어 "uint16_t"타입의 변수 "x"를 원한다면 "x"는 2 바이트의 공간만을 차지할 것입니다 (그리고 타입과 같은 숨겨진 정보는 포함하지 않을 것입니다) ). 마찬가지로 100 개의 정수 배열을 원한다면 100 개의 정수만큼 클 수 있습니다.

그러나, 더 나는 그것이 사실이 있는지 더 궁금하고이 기능에 대한 구체적인 사용 사례를 마련하기 위해 노력하고 있는 모두에 실질적인 이점을. 지금까지 내가 올 수 있었던 유일한 것은 분명히 적은 RAM이 필요 하다는 것 입니다. AVR 칩 등과 ​​같은 제한된 환경의 경우 이는 분명히 큰 이점이지만 일상적인 데스크탑 / 서버 사용 사례의 경우에는 다소 관련이없는 것 같습니다. 내가 생각하고있는 또 다른 가능성은 하드웨어에 액세스하거나 메모리 영역을 매핑하는 데 도움이 / 중요 할 있다는 것입니다 (예 : VGA 출력 등) ...?

내 질문 :이 기능이 없으면 구현이 불가능하거나 매우 성 가실 수있는 구체적인 도메인이 있습니까?

추신 : 당신이 더 나은 이름을 가지고 있다면 알려주십시오! ;)



@ gnat 나는 당신의 문제가 무엇인지 이해한다고 생각합니다. 여러 답변이있을 수 있기 때문입니다. 글쎄, 나는이 질문이 stackexchange가 작동하는 방식에 맞지 않을 수도 있지만, 솔직히 어디에서 물어볼 지 모르겠습니다 ...
Thomas Oltmann

1
@lxrec RTTI는 vtable에 저장되며 객체는 vtable에 대한 포인터 만 저장합니다. 또한 타입은 virtual멤버 함수 를 가지고 있기 때문에 이미 vtable이있는 경우 RTTI 만 있습니다 . 따라서 RTTI는 객체의 크기를 늘리지 않으며 바이너리를 상수만큼 크게 만듭니다.

3
@ThomasOltmann 가상 메서드 가있는 모든 개체 에는 vtable 포인터가 필요합니다. 그것 없이는 기능적인 가상 메소드를 가질 수 없습니다. 또한 가상 메서드 (따라서 vtable)를 사용하도록 명시 적으로 선택합니다.

1
@ThomasOltmann 당신은 매우 혼란스러워 보인다. vtable 포인터를 전달하는 객체에 대한 포인터가 아니라 객체 자체입니다. 즉, T *항상 같은 크기이며 Tvtable을 가리키는 숨겨진 필드가 포함될 수 있습니다. 그리고 C ++ 컴파일러는 vtable을 필요없는 객체에 삽입하지 않았습니다.

답변:


5

함수 매개 변수와 같은 것들이 전달되는 값과 일치하는지 확인하기 위해 컴파일 타임에 확실한 이점이 있습니다.

그러나 런타임에 무슨 일이 일어나고 있는지 묻는 것 같습니다.

컴파일러는 수행하는 작업에 데이터 형식에 대한 지식을 포함하는 런타임을 만듭니다. 메모리의 각 데이터 청크는 자체 설명이 아닐 수 있지만 코드는 본질적으로 해당 데이터가 무엇인지 알고 있습니다 (작업을 올바르게 수행 한 경우).

런타임에는 상황이 약간 다릅니다.

예를 들어 uint16_t를 선언 할 때 2 바이트 만 사용한다고 가정하지 마십시오. 프로세서 및 워드 정렬에 따라 스택에서 16, 32 또는 64 비트를 차지할 수 있습니다. 반바지 배열이 예상보다 훨씬 많은 메모리를 소비한다는 것을 알 수 있습니다.

특정 오프셋에서 데이터를 참조해야하는 특정 상황에서는 문제가 될 수 있습니다. 무선 링크 또는 파일을 통해 프로세서 아키텍처가 다른 두 시스템간에 통신 할 때 발생합니다.

C를 사용하면 비트 레벨 입도로 구조체를 지정할 수 있습니다.

struct myMessage {
  uint8_t   first_bit: 1;
  uint8_t   second_bit: 1;
  uint8_t   padding:6;
  uint16_t  somethingUseful;
}

이 구조는 길이가 3 바이트이며 홀수 오프셋에서 시작하도록 짧게 정의됩니다. 또한 정의한대로 정확하게 포장해야합니다. 그렇지 않으면 컴파일러가 멤버를 단어 정렬합니다.

컴파일러는 배후에서 코드를 생성하여이 데이터를 추출하고 레지스터에 복사하여 유용한 작업을 수행 할 수 있습니다.

이제 내 프로그램이 myMessage 구조체의 멤버에 액세스 할 때마다이를 추출하고 조작하는 방법을 정확히 알 수 있습니다.

다른 버전의 소프트웨어로 다른 시스템간에 통신 할 때 문제가되고 관리하기가 어려워 질 수 있습니다. 시스템과 코드를주의 깊게 설계하여 양쪽이 데이터 유형에 대해 정확히 동일한 정의를 갖도록해야합니다. 일부 환경에서는이 작업이 매우 어려울 수 있습니다. 여기에는 Google의 프로토콜 버퍼 와 같은 자체 설명 데이터가 포함 된 더 나은 프로토콜이 필요합니다 .

마지막으로 데스크탑 / 서버 환경에서 이것이 얼마나 중요한지 물어볼 수 있습니다. 실제로 사용하려는 메모리 양에 따라 다릅니다. 이미지 처리와 같은 작업을 수행하는 경우 응용 프로그램 성능에 영향을 줄 수있는 많은 양의 메모리를 사용하게 될 수 있습니다. 이것은 메모리가 제한되고 가상 메모리가없는 임베디드 환경에서 항상 우려되는 사항입니다.


2
"짧은 반바지 배열이 예상보다 훨씬 많은 메모리를 소비한다는 것을 알게 될 것입니다." 이것은 C에서 잘못되었습니다 : 배열은 틈이없는 방식으로 요소를 포함합니다. 예, single과 마찬가지로 배열을 올바르게 정렬해야합니다 short. 그러나 이것은 배열의 시작에 대한 일회성 요구 사항이며 나머지는 연속적이므로 자동으로 올바르게 정렬됩니다.
cmaster-복원 monica

또한 패딩 구문이 잘못되었습니다 uint8_t padding: 6;. 첫 두 비트와 동일 해야합니다 . 또는 더 명확하게, 단지 주석 //6 bits of padding inserted by the compiler입니다. 작성한 구조의 크기는 3 바이트가 아닌 9 바이트 이상입니다.
cmaster-복원 monica

9

이것이 유용한 유일한 이유 중 하나 인 외부 데이터 구조의 매핑입니다. 여기에는 메모리 매핑 비디오 버퍼, 하드웨어 레지스터 이 포함됩니다 . 여기에는 SSL 인증서, IP 패킷, JPEG 이미지 및 프로그램 외부에서 지속적으로 사용되는 거의 모든 기타 데이터 구조와 같이 프로그램 외부로 그대로 전송 된 데이터도 포함됩니다.


5

C는 저수준 언어로 거의 이식 가능한 어셈블러이므로 데이터 구조와 언어 구조가 금속에 가깝습니다 (데이터 구조는 하드웨어와 ABI가 부과하는 패딩, 정렬 및 크기 제약을 제외하고 숨겨진 비용이 없습니다 ). 따라서 C에는 기본적으로 동적 입력이 없습니다. 그러나 필요한 경우 모든 값이 일부 유형 정보 (예 : ...)로 시작하는 집계 라는 규칙 을 채택 할 수 있습니다 . 사용 -s와 (배열과 같은 것들을) 가요 성 부재 배열 에서 같은 배열의 크기를 포함.enumunionstruct

(C로 프로그래밍 할 때는 특히 사전 및 사후 조건 및 불변 인과 같은 유용한 규칙을 정의, 문서화 및 준수하는 것이 귀하의 책임입니다. 또한 C 동적 메모리 할당 에는 free힙이있는 malloc메모리 영역을 누가 사용해야하는지에 대한 명시 적 규칙이 필요 합니다)

따라서 박스형 정수, 문자열 또는 어떤 종류의 구성표 와 같은 기호 또는 값의 벡터 인 값 을 나타 내기 위해 개념적으로 유형 결합으로 시작되는 태그 결합 (포인터 결합으로 구현 됨)을 개념적으로 사용합니다. -예 :

enum value_kind_en {V_NONE, V_INT, V_STRING, V_SYMBOL, V_VECTOR};
union value_en { // this union takes a word in memory
   const void* vptr; // generic pointer, e.g. to free it
   enum value_kind_en* vkind; // the value of *vkind decides which member to use
   struct intvalue_st* vint;
   struct strvalue_st* vstr;
   struct symbvalue_st* vsymb;
   struct vectvalue_st* vvect;
};
typedef union value_en value_t;
#define NULL_VALUE  ((value_t){NULL})
struct intvalue_st {
  enum value_kind_en kind; // always V_INT for intvalue_st
  int num;
};
struct strvalue_st {
  enum value_kind_en kind; // always V_STRING for strvalue_st
  const char*str;
};
struct symbvalue_st {
  enum value_kind_en kind; // V_SYMBOL
  struct strvalue_st* symbname;
  value_t symbvalue;
};
struct vectvalue_st {
  enum value_kind_en kind; // V_VECTOR;
  unsigned veclength;
  value_t veccomp[]; // flexible array of veclength components.
};

일부 값의 동적 유형을 얻으려면

enum value_kind_en value_type(value_t v) {
  if (v.vptr != NULL) return *(v.vkind);
  else return V_NONE;
}

다음은 벡터에 대한 "동적 캐스트"입니다.

struct vectvalue_st* dyncast_vector (value_t v) {
   if (value_type(v) == V_VECTOR) return v->vvect;
   else return NULL;
}

벡터 내부의 "안전한 접근 자":

value_t vector_nth(value_t v, unsigned rk) {
   struct vectvalue_st* vecp = dyncast_vector(v);
   if (vecp && rk < vecp->veclength) return vecp->veccomp[rk];
   else return NULL_VALUE;
}

일반적으로 static inline일부 헤더 파일에서 와 같이 대부분의 짧은 기능을 정의 합니다.

BTW, Boehm의 가비지 콜렉터 를 사용할 수 있으면 상위 레벨 (하지만 안전하지 않은) 스타일로 아주 쉽게 코딩 할 수 있으며 여러 Scheme 인터프리터가 그런 식으로 수행됩니다. 가변 벡터 생성자는

value_t make_vector(unsigned size, ... /*value_t arguments*/) {
   struct vectvalue_st* vec = GC_MALLOC(sizeof(*vec)+size*sizeof(value));
   vec->kind = V_VECTOR;
   va_args args;
   va_start (args, size);
   for (unsigned ix=0; ix<size; ix++) 
     vec->veccomp[ix] = va_arg(args,value_t);
   va_end (args);
   return (value_t){vec};
}

세 개의 변수가 있다면

value_t v1 = somevalue(), v2 = otherval(), v3 = NULL_VALUE;

당신은 그들을 사용하여 그들로부터 벡터를 구축 할 수 있습니다 make_vector(3,v1,v2,v3)

Boehm의 가비지 수집기를 사용하지 않으려면 (또는 직접 디자인) 소멸자를 정의하고 메모리를 누가, 어떻게, 언제 기억해야하는지 문서화하는 데 매우주의해야합니다 free. 예를 참조하십시오 . 따라서 위 malloc대신에 (그러나 실패에 대해 테스트 할 수는 있지만) GC_MALLOC소멸자 함수를 신중하게 정의하고 사용해야합니다.void destroy_value(value_t)

C의 강점은 위와 같은 코드를 가능하게하고 자신의 규약 (특히 소프트웨어)을 정의하기에 충분히 낮은 수준입니다.


내 질문을 오해 한 것 같아 C로 동적으로 타이핑하는 것을 원하지 않습니다. C 의이 특정 속성이 실제로 사용되는지 궁금합니다.
Thomas Oltmann

그러나 C의 정확한 속성은 무엇입니까? C 데이터 구조는 금속에 가깝기 때문에 숨겨진 비용이 없습니다 (정렬 및 크기 제한 제외)
Basile Starynkevitch

정확히 : :
Thomas Oltmann

C는 저수준 언어로 발명되었지만 gcc와 같은 컴파일러에서 최적화를 설정하면 저수준 구문을 사용하지만 플랫폼이 제공하는 동작 보장에 대한 저수준 액세스를 안정적으로 제공하지 않는 언어를 처리합니다. malloc과 memcpy를 사용하려면 sizeof가 필요하지만, 더 현대적인 주소 계산에서는 "modern"C에서 지원되지 않을 수 있습니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.