Łatwo jest powiedzieć, co się dzieje, patrząc na available LDC instructions. Zwróć uwagę na ograniczony zestaw dostępnych typów argumentów, dostępna jest wersja no, która ładuje stałą typu short. Po prostu int, long, float i double. Ograniczenia te są widoczne w innym miejscu, na przykład instrukcja Opcodes.Add jest podobnie ograniczona, brak obsługi dodawania zmiennych jednego z mniejszych typów.
Zestaw instrukcji IL został zaprojektowany bardzo celowo w ten sposób, odzwierciedla możliwości prostego procesora 32-bitowego. Rodzaj procesora, o którym można pomyśleć, to rodzaj RISC, mieli dzień siana w dziewiętnastu latach. Wiele 32-bitowych rejestrów cpu, które mogą manipulować tylko 32-bitowymi liczbami całkowitymi i typami zmiennoprzecinkowymi IEEE-754. Rdzeń Intel x86 nie jest dobrym przykładem, a bardzo często jest to projekt CISC, który faktycznie obsługuje ładowanie i robienie arytmetyki na operandach 8-bitowych i 16-bitowych. Ale to bardziej historyczny przypadek, dzięki któremu łatwe tłumaczenie mechaniczne programów rozpoczęło się na 8-bitowych 8080 i 16-bitowych procesorach 8086. Ale taka możliwość nie przychodzi za darmo, manipulowanie 16-bitowymi wartościami faktycznie kosztuje dodatkowy cykl procesora.
Dopasowanie IL do 32-bitowych możliwości procesora znacznie ułatwia zadanie facetowi wdrażającemu jitter. Miejsca w magazynie mogą nadal być mniejsze, ale tylko obciążenia, sklepy i konwersje muszą być obsługiwane. I tylko wtedy, gdy jest to potrzebne, twoja zmienna "a" jest zmienną lokalną, która w każdym razie zajmuje 32 bity na ramce stosu lub rejestrze cpu.Tylko magazyny do pamięci muszą być przycięte do odpowiedniego rozmiaru.
W fragmencie kodu nie ma żadnych niejednoznaczności. Wartość zmiennej musi zostać podzielona, ponieważ Marshal.SizeOf() przyjmuje argument typu obiekt. Wartość pudełkowa identyfikuje typ wartości przez uchwyt typu, wskaże System.Int16. Marshal.SizeOf() ma wbudowaną wiedzę, która wie, że zajmuje 2 bajty.
Te ograniczenia odzwierciedlają język C# i powodują niespójność. Ten rodzaj błędu kompilacji zawsze befuddles i irytuje programistów C#:
byte b1 = 127;
b1 += 1; // no error
b1 = b1 + 1; // error CS0266
co wynika z ograniczeń IL, nie ma operator dodać, że trwa argumentów bajtów. Muszą one zostać przekonwertowane na następny większy zgodny typ: int w tym przypadku. Tak działa na 32-bitowym procesorze RISC. Teraz jest problem, 32-bitowy wynik musi zostać wbity z powrotem w zmienną, która może przechowywać tylko 8-bitów. Język C# stosuje ten sam młotek w pierwszym zadaniu, ale nielogicznie wymaga odlewania w drugim zadaniu.
Rozmiar w pamięci jest tylko znaczącą koncepcją po umieszczeniu jej w większej strukturze, na przykład w tablicy. Kiedy masz zmienną lokalną, zwykle zajmuje ona pełny rejestr (64 bity na AMD64), nawet jeśli jest to tylko jeden bajt. Kompilator C# używa Int32 wewnętrznie dla większości rzeczy, co jest w jego prawach tak długo, jak obserwowane zachowanie pasuje do tego z Int16. – CodesInChaos
@CodesInChaos Więc zajmuje 4 bajty w 32-bitowym? a jeśli tak, dlaczego sieOf pokazuje 2? –
Rozmiar wynosi 2, ponieważ jest zdefiniowany w ten sposób, a 2 bajty są wystarczające. Ale jeśli masz zmienną lokalną, zajmuje ona tyle bajtów, ile potrzebuje kompilator/JITer. Dopóki nie wpłynie to na działanie programu, mogą robić to, co im się podoba. Często pojedyncza zmienna lokalna jest przechowywana w różnych miejscach podczas uruchamiania metody. W twoim przypadku mogą nawet całkowicie wyeliminować 'a' i po prostu użyć stałego' 2'. – CodesInChaos