C'nin değişkenlerin “gerçek” boyutlandırması ne kadar yararlıdır?


9

Beni her zaman sezgisel olarak C'nin olumlu bir özelliği olarak (şey, aslında gcc, clang, ... gibi uygulamaların) vuran bir şey, çalışma zamanında kendi değişkenlerinizin yanında gizli bilgi depolamamasıdır. Bununla demek istediğim, örneğin "uint16_t" türünde bir "x" değişkeni istediyseniz, "x" değerinin yalnızca 2 bayt yer kaplayacağından (ve türü gibi gizli bilgileri taşımayacağından) emin olabilirsiniz. .). Benzer şekilde, 100 tamsayı bir dizi istiyorsanız, 100 tamsayı kadar büyük olduğundan emin olabilirsiniz.

Ancak, bu özellik için somut kullanım durumları ortaya çıkarmaya çalıştığımda, aslında herhangi bir pratik avantajı olup olmadığını merak ediyorum . Ben şimdiye kadar ile gelebilir tek şey olduğunu açıkça daha az RAM ihtiyacı vardır. AVR çipleri vb. Gibi sınırlı ortamlar için bu kesinlikle büyük bir artıdır, ancak günlük masaüstü / sunucu kullanım durumları için oldukça alakasız görünmektedir. Düşündüğüm bir başka olasılık , donanıma erişmek veya belki de bellek bölgelerini (örneğin VGA çıkışı ve benzerleri için) eşleştirmek için yararlı / önemli olabilir ...?

Benim sorum: Bu özellik olmadan ya çok hantal bir şekilde uygulanamayan ya da uygulanamayan somut alanlar var mı?

PS Lütfen bunun için daha iyi bir ismin olup olmadığını söyle! ;)



@gnat Sanırım sorununun ne olduğunu anlıyorum. Çünkü birden fazla cevap olabilir, değil mi? Eh, bu sorunun
stackexchange'in

1
@lxrec RTTI vtable içinde saklanır ve nesneler vtable'a yalnızca bir işaretçi depolar. Buna ek olarak, türler yalnızca virtualüye bir işleve sahip oldukları için zaten bir vtable varsa RTTI'ye sahiptir . Bu nedenle RTTI hiçbir nesnenin boyutunu asla arttırmaz, sadece ikiliyi sabit bir şekilde büyütür.

3
@ThomasOltmann Sanal yöntemlere sahip her nesnenin bir vtable işaretçisine ihtiyacı vardır . Bu olmadan sanal yöntemler işlevselliğine sahip olamazsınız. Dahası, açıkça sanal yöntemlere (ve dolayısıyla bir vtable) sahip olmayı tercih edersiniz.

1
@ThomasOltmann Çok karışık görünüyorsun. Bu bir değişken işaretçi taşıyan bir nesneye işaretçi değil, nesnenin kendisidir. Yani, T *her zaman aynı boyuttadır ve Tvtable'ı gösteren gizli bir alan içerebilir. Ve hiçbir C ++ derleyicisi hiç ihtiyaç duymayan nesnelere vtables eklemedi.

Yanıtlar:


5

Birkaç avantajı vardır, açık olan, işlev parametreleri gibi şeylerin iletilen değerlerle eşleştiğinden emin olmak için derleme zamanındadır.

Ama sanırım çalışma zamanında neler olduğunu soruyorsun.

Derleyicinin, gerçekleştirdiği işlemlere veri türleri hakkındaki bilgileri dahil eden bir çalışma zamanı oluşturacağını unutmayın. Bellekteki her veri parçası kendi kendini tanımlamayabilir, ancak kod doğal olarak bu verilerin ne olduğunu bilir (işinizi doğru bir şekilde yaptıysanız).

Çalışma zamanında işler düşündüğünüzden biraz farklıdır.

Örneğin, uint16_t bildirirken yalnızca iki bayt kullanıldığını düşünmeyin. İşlemci ve sözcük hizalamasına bağlı olarak, yığın üzerinde 16, 32 veya 64 bit içerebilir. Şort dizinizin beklediğinizden çok daha fazla bellek tükettiğini görebilirsiniz.

Bu, belirli ofsetlerdeki verilere başvurmanız gereken bazı durumlarda sorunlu olabilir. Bu, kablosuz işlemci veya dosyalar aracılığıyla farklı işlemci mimarilerine sahip iki sistem arasında iletişim kurarken olur.

C, bit düzeyinde ayrıntı düzeyine sahip yapıları belirtmenize olanak tanır:

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

Bu yapı üç bayt uzunluğundadır ve kısa bir süre tek bir ofsetten başlamak üzere tanımlanmıştır. Tam olarak tanımladığınız gibi paketlenmesi de gerekir. Aksi takdirde derleyici üyeleri sözcük olarak hizalar.

Derleyici, bu verileri ayıklamak ve bir kayıt defterine kopyalamak için sahne arkasında kod oluşturur, böylece yararlı şeyler yapabilirsiniz.

Artık programımın myMessage yapısının bir üyesine her eriştiğinde, onu tam olarak nasıl ayıklayacağını ve üzerinde çalışacağını bileceğini görebilirsiniz.

Bu, farklı yazılım sürümlerine sahip farklı sistemler arasında iletişim kurarken sorunlu ve yönetilmesi zor olabilir. Her iki tarafın da veri türlerinin tam olarak aynı tanımına sahip olmasını sağlamak için sistemi ve kodu dikkatlice tasarlamalısınız. Bu, bazı ortamlarda oldukça zor olabilir. Google'ın Protokol Tamponları gibi kendi kendini tanımlayan verileri içeren daha iyi bir protokole ihtiyaç duyduğunuz yer burasıdır .

Son olarak, bunun masaüstü / sunucu ortamında ne kadar önemli olduğunu sormak için iyi bir noktaya değindiniz. Gerçekten ne kadar bellek kullanmayı planladığınıza bağlıdır. Görüntü işleme gibi bir şey yapıyorsanız, uygulamanızın performansını etkileyebilecek büyük miktarda bellek kullanabilirsiniz. Bu kesinlikle hafızanın kısıtlandığı ve sanal hafızanın olmadığı gömülü ortamda her zaman bir endişe kaynağıdır.


2
"Şort dizinizin beklediğinizden çok daha fazla bellek tükettiğini görebilirsiniz." C'de bu yanlış: Dizilerin elemanlarını boşluksuz bir şekilde içermeleri garanti edilir. Evet, dizinin tek bir dizinin yaptığı gibi düzgün hizalanması gerekir short. Ancak bu, dizinin başlangıcı için bir kerelik bir gereksinimdir, geri kalanlar ardışık olması nedeniyle otomatik olarak doğru şekilde hizalanır.
cmaster - eski haline monica

Ayrıca, dolgu için sözdizimi yanlıştır, uint8_t padding: 6;tıpkı ilk iki bit gibi. Ya da, daha açık bir şekilde, sadece yorum //6 bits of padding inserted by the compiler. Yapı, yazdığınız gibi, üç değil, en az dokuz bayt boyutundadır.
cmaster - eski haline monica

9

Bunun yararlı olmasının tek nedenlerinden birini seçtiniz: dış veri yapılarını eşleme. Bunlar arasında bellek eşlemeli video arabellekleri, donanım kayıtları vb. Bulunur. Ayrıca, SSL sertifikaları, IP paketleri, JPEG görüntüleri ve programın dışında kalıcı bir ömre sahip olan hemen hemen tüm veri yapıları gibi programın dışında bozulmadan iletilen verileri de içerir.


5

C düşük seviyeli bir dildir, neredeyse taşınabilir bir montajcıdır, bu nedenle veri yapıları ve dil yapıları metale yakındır (veri yapılarının gizli bir maliyeti yoktur - dolgu ve hizalama ve donanım ve ABI tarafından uygulanan boyut kısıtlamaları hariç ). C gerçekten de doğal olarak dinamik yazmaya sahip değildir. Ama bunu gerekirse, bir evlat edinebiliriz kongre tüm değerler bir tür bilgilerle başlayan agrega olduğunu (örneğin bazı enum...); Kullanım union-s ve (dizi benzeri şeyleri) esnek dizi üyesi olarak structda dizinin boyutu ihtiva etmektedir.

(C dilinde programlama yapılırken, özellikle ön koşullar ve son koşullar ve değişmezler gibi yararlı kuralları tanımlamak, belgelemek ve takip etmek sizin sorumluluğunuzdadır; ayrıca C dinamik bellek tahsisi , bir freemiktar hafızalı mallocbellek bölgesinin kiminle ilgili olması gerektiğini anlatan sözleşmeler gerektirir )

Bu nedenle, kutulu tamsayılar veya dizeler veya bir tür Şema benzeri sembol veya değer vektörleri olan değerleri temsil etmek için, kavramsal olarak, tür türünden başlayarak her zaman etiketli bir birlik (işaretçiler birliği olarak uygulanır) kullanırsınız. -, Örneğin:

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.
};

Bazı değerlerin dinamik türünü elde etmek için

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

İşte vektörlere "dinamik bir döküm":

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

ve vektörlerin içinde "güvenli erişimci":

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;
}

Yukarıdaki kısa işlevlerin çoğunu static inlinebazı üstbilgi dosyalarında olduğu gibi tanımlarsınız .

BTW, Boehm'in çöp toplayıcısını kullanabiliyorsanız, daha üst düzey (ancak güvensiz) bir tarzda kolayca kodlayabilirsiniz ve birkaç Şema tercümanı bu şekilde yapılır. Değişken bir vektör oluşturucu

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};
}

ve üç değişkeniniz varsa

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

kullanarak onlardan bir vektör oluşturabilirsin make_vector(3,v1,v2,v3)

Boehm'in çöp toplayıcısını kullanmak istemiyorsanız (veya kendinizinkini tasarlayın) yıkıcıları tanımlamaya ve hafızanın kim, nasıl ve ne zaman olması gerektiğini belgelemeye çok dikkat etmelisiniz free; bu örneğe bakın . Bu yüzden yukarıdaki mallocyerine kullanabilirsiniz (ancak başarısızlığına karşı test edin), GC_MALLOCancak bazı yıkıcı işlevlerini dikkatlice tanımlamanız ve kullanmanız gerekir.void destroy_value(value_t)

C'nin gücü, yukarıdaki gibi kodu mümkün kılmak ve kendi kurallarınızı (yazılımınıza özel) tanımlamak için yeterince düşük seviyededir.


Sanırım sorumu yanlış anladın. C'de dinamik yazmayı istemiyorum. C'nin bu özel özelliğinin herhangi bir pratik kullanım olup olmadığını merak ettim.
Thomas Oltmann

Fakat C'nin hangi özelliğinden bahsediyorsunuz? C veri yapıları metale yakındır, bu nedenle hiçbir gizli maliyeti yoktur (hizalama ve boyut kısıtlamaları hariç)
Basile Starynkevitch

Tam olarak: /
Thomas Oltmann

C, düşük düzeyli bir dil olarak icat edildi, ancak optimizasyonlar gcc gibi derleyiciler açıldığında, düşük düzeyli sözdizimini kullanan ancak platform tarafından sağlanan davranışsal garantilere güvenilir bir şekilde düşük düzeyli erişim sağlamayan bir dil. Malloc ve memcpy kullanmak için bir
boyuta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.