C의 “실제”크기 조정 변수는 얼마나 유용합니까? 원한다면 “x”는 2 바이트의 공간만을 차지할

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

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

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

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



답변

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

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

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

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

예를 들어 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의 프로토콜 버퍼 와 같은 자체 설명 데이터가 포함 된 더 나은 프로토콜이 필요합니다 .

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


답변

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


답변

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


답변