C'de yapısal bellek düzeni


86

C # geçmişim var. C gibi düşük seviyeli bir dil için çok acemiyim.

C # struct'da , belleği varsayılan olarak derleyici tarafından düzenlenir. Derleyici, veri alanlarını yeniden sıralayabilir veya alanlar arasında örtük olarak ek bitler doldurabilir. Bu nedenle, tam düzen için bu davranışı geçersiz kılmak için bazı özel nitelikler belirtmem gerekiyordu.

AFAIK, C structvarsayılan olarak a'nın bellek düzenini yeniden sıralamaz veya hizalamaz . Ancak, bulması çok zor olan küçük bir istisna olduğunu duydum.

C'nin bellek düzeni davranışı nedir? Ne yeniden sıralanmalı / hizalanmalı ve değil

Yanıtlar:


111

C'de, derleyicinin her ilkel tür için bir miktar hizalama dikte etmesine izin verilir. Tipik olarak hizalama, türün boyutudur. Ancak tamamen uygulamaya özgüdür.

Dolgu baytları eklenir, böylece her nesne uygun şekilde hizalanır. Yeniden sıralamaya izin verilmiyor.

Muhtemelen her uzaktan modern derleyici #pragma pack, dolgu üzerinde kontrole izin veren ve ABI ile uyum sağlamak için onu programcıya bırakan uygular . (Yine de kesinlikle standart değildir.)

C99 §6.7.2.1'den:

12 Bir yapının veya birleşim nesnesinin bit alanı olmayan her üyesi, türüne uygun uygulama tanımlı bir şekilde hizalanır.

13 Bir yapı nesnesi içinde, bit alanı olmayan üyeler ve bit alanlarının bulunduğu birimler, bildirildikleri sırada artan adreslere sahiptir. Uygun şekilde dönüştürülmüş bir yapı nesnesine bir işaretçi, başlangıç ​​üyesine (veya bu üye bir bit alanı ise, o zaman içinde bulunduğu birime) işaret eder ve bunun tersi de geçerlidir. Bir yapı nesnesi içinde adsız dolgu olabilir, ancak başlangıcında olmayabilir.


1
Bazı derleyiciler (yani GCC), aynı etkiyi uygular, #pragma packancak anlambilim üzerinde daha ayrıntılı denetim sağlar.
Chris Lutz

21
Olumsuz oy görünce şaşırdım. Herhangi biri hatayı gösterebilir mi?
Potatoswatter

2
C11'de de var _Alignas.
idmean

117

Uygulamaya özgüdür, ancak pratikte kural ( #pragma packyoksa veya benzeri bir durumda):

  • Struct üyeleri, bildirildikleri sırayla saklanır. (Bu, daha önce bahsedildiği gibi C99 standardı tarafından gereklidir.)
  • Gerekirse, doğru hizalamayı sağlamak için her yapı üyesinden önce dolgu eklenir.
  • Her ilkel tip T, sizeof(T)baytların hizalanmasını gerektirir .

Öyleyse, aşağıdaki yapı verildiğinde:

  • ch1 ofset 0'da
  • hizalamak için bir dolgu baytı eklenir ...
  • s ofset 2'de
  • ch2 s'den hemen sonra ofset 4'te
  • Hizalamak için 3 dolgu baytı eklenir ...
  • ll ofset 8'de
  • i 11'den hemen sonra ofset 16'da
  • Sona 4 dolgu baytı eklenir, böylece genel yapı 8 baytın katı olur. Bunu 64 bitlik bir sistemde kontrol ettim: 32 bit sistemler yapıların 4 bayt hizalamasına izin verebilir.

Yani sizeof(ST)24 olduğunu.

Doldurmayı önlemek için üyeleri yeniden düzenleyerek 16 bayta düşürülebilir:


3
Gerekirse, dolgudan önce eklenir ... Daha çok sonra gibi. En iyisi charörneğinize son bir üye ekleyin .
Deduplicator

9
İlkel bir tür, sizeof(T)baytların hizalanmasını gerektirmez . Örneğin, doubleyaygın 32 bit mimarilerde 8 bayttır, ancak genellikle yalnızca 4 baytlık hizalama gerektirir . Ayrıca, yapının sonundaki dolgu, yalnızca en geniş yapı üyesinin hizalamasını destekler. Örneğin, 3 karakterli bir yapı değişkeninin dolgusu olmayabilir.
Matt

1
@ dan04, yapıları sizeof (T) 'nin azalan sırasına göre düzenlemek iyi bir uygulama olur. Bunu yapmanın herhangi bir dezavantajı var mı?
RohitMat

11

Veri hizalamasını daha iyi anlamak için veri yapısı hizalama wikipedia makalesini okuyarak başlayabilirsiniz .

Gönderen wikipedia makalesinde :

Veri hizalama, verileri kelime boyutunun birkaç katına eşit bir bellek ofsetine koymak anlamına gelir, bu da CPU'nun belleği işleme biçimi nedeniyle sistemin performansını artırır. Verileri hizalamak için, son veri yapısının sonu ile bir sonrakinin başlangıcı arasına bazı anlamsız baytlar eklemek gerekebilir, bu da veri yapısı dolgusudur.

Gönderen 6.54.8 Yapı-Ambalaj Pragmas GCC belgelerin:

Microsoft Windows derleyicileriyle uyumluluk için GCC, yapı üyelerinin (sıfır genişlikli bit alanları dışında), birliklerin ve sonradan tanımlanan sınıfların maksimum hizalamasını değiştiren bir dizi #pragma yönergesini destekler. Aşağıdaki n değerinin her zaman ikinin küçük bir üssü olması gerekir ve yeni hizalamayı bayt cinsinden belirtir.

  1. #pragma pack(n) basitçe yeni hizalamayı ayarlar.
  2. #pragma pack() hizalamayı derleme başladığında geçerli olana ayarlar (ayrıca bkz. komut satırı seçeneği -fpack-struct [=] bkz. Kod Oluşturma Seçenekleri).
  3. #pragma pack(push[,n]) mevcut hizalama ayarını dahili bir yığına iter ve ardından isteğe bağlı olarak yeni hizalamayı ayarlar.
  4. #pragma pack(pop)hizalama ayarını dahili yığının en üstünde kaydedilene geri yükler (ve bu yığın girişini kaldırır). #pragma pack([n])Bu dahili yığını etkilemediğini unutmayın ; bu nedenle #pragma pack(push) birden fazla #pragma pack(n) örnekle takip edilmiş ve tek bir ile sonuçlandırılmış olabilir #pragma pack(pop).

Bazı hedefler, örneğin i386 ve powerpc, #pragmabir yapıyı belgelendiği gibi düzenleyen ms_struct'ı destekler __attribute__ ((ms_struct)).

  1. #pragma ms_struct on bildirilen yapılar için düzeni açar.
  2. #pragma ms_struct off bildirilen yapılar için düzeni kapatır.
  3. #pragma ms_struct reset varsayılan düzene geri döner.

İlgin için teşekkürler. Size rehberlik ederken soruyu değiştirdim.
eonil
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.