Aktywne Wpisy
![mickpl](https://wykop.pl/cdn/c0834752/e8a8471ee0ef9099b528719cfb0a12bfb11959242b197c0a5ccb8689ed0e1cc8,q60.jpg)
mickpl +716
![IntruderXXL](https://wykop.pl/cdn/c3397992/IntruderXXL_mknh7Lyz1x,q60.jpg)
IntruderXXL +313
TOTAL 1000KG treningowo. A oficjalny rekord Polski w trójboju wynosi... 1000kg.
Przysiad - 370kg
Wyciskanie leżąc - 265kg
Martwy ciąg - 365kg
Stało
Przysiad - 370kg
Wyciskanie leżąc - 265kg
Martwy ciąg - 365kg
Stało
![IntruderXXL - TOTAL 1000KG treningowo. A oficjalny rekord Polski w trójboju wynosi......](https://wykop.pl/cdn/c3201142/258295019abb66ceada09bac02926502c68a9262c4e5e0630975a38fb154203e,w150h100.jpg?author=IntruderXXL&auth=a6001343dde1880a177b7ad056f27a6f)
Dzisiaj mam dla was kolejną wskazówkę z serii #zloteradypassera w temacie języka C++.
Najczęściej piszę o różnych featurach języka, ale dzisiaj będzie o pewnym błędzie w implementacji kompilatorów, który jest na tyle powszechny, że możemy spokojnie go użyć do swoich potrzeb na wielu architekturach. Mowa o tzw memory alignment.
Jak zapewne wszyscy wiemy, każdy obiekt w C++ ma określony rozmiar który zajmuje w pamięci w zależności od typu. Czasami jest to określone bezpośrednio jak to, że
char
ma zawsze rozmiar 1, czasami jest to określone pośrednio jak to, żeint
ma rozmiar "przynajmniej 16bitów" chociaż dobrze wiemy, że w większości przypadków jest to rozmiar 32 bitów. Dla uproszczenia tego wpisu załóżmy. że int jest zawsze 32 bitowy i korzystamy z architektury amd64.Wyboraźmy sobie zatem, że mamy dwie zmiennie, jedną typu
char
i drugą typuint
. Jak łatwo policzyć pierwsza ma rozmiar 1 bajta, druga 4 bajty. Łatwo to sprawdzić korzystając z operatorasizeof
.Co się jednak stanie kiedy obie te zmienne umieścimy w jednej strukturze? Otóż można by przypuszczać, że rozmiar będzie wynosił 1 + 4 czyli 5. Otóż nic bardziej mylnego ( ͡º ͜ʖ͡º) Dzięki czemu coś jest znane jako struct padding/memory alignment rozmiar całej struktury będzie wynosił 8. Dlaczego tak się dzieje?
Otóż
memory alignment
oznacza wyrównianie pamięci, ale z moich obserwacji wynika że na wszystkich kompilatorach które przetestowałem (Clang, GCC, MSVC) jest to zbugowane! Żadna pamieć nie jest wyrównana! Obie zmienne są rozjechane w pamięci a pomiędzy nimi jest luka!Na początku to odkrycie było dla mnie bardzo frustrujące, ale nauczony doświadczeniem postawiłem to wykorzystać ku swojej korzyści i okazuje się że w tej luce (z angielska nazywanej paddingiem) możemy przechowywać dodatkowe dane, nie zwiekszając rozmiaru całej struktury ( ͡º ͜ʖ͡º)
Przykładowy kod demonstruje tą technikę. Jak widać na standardowym wyjściu struktura ma rozmiar 8 bajtów, chociaż same zmienne typu
char
iint
zajmują ledwie 5 tak jak policzyliśmy wcześniej. Mamy więc dodatkowe 3 bajty do wykorzystania.W tym przypadku wykorzystuje bajt nr 3 i przypisuje mu wartość 5. Jest to mała wartość więc spokojnie się tam zmieści.
Następnie mogę to wykorzystać tak jak każdy inny obiekt typu int, w tym przypadku w wyrażeniu numerycznym.
Wykorzystywanie tego błedu kompilatorów może być szczególnie przydatne jeśli projektumy interfejsy/API, bo możemy przechowywać dowolne dane w lukach/dziurach, a użytkownik interfejsu nie będzie ich świadomy. Tak naprawdę bardzo mało programistów C++ (wliczają w to mnie) zagląda w te dziury.
Nie ma jednak róży bez kolców więc jako uczciwy mentor musze też wspomnieć o dwóch ważnych kwestiach abusowania tego buga:
- konstruktory kopiujące nie zawsze poprawnie kopiują dane w dziurach, więc do kopiowania takich obiektów trzeba użyć
std::memcpy
, ale jest to też pewnego rodzaju zaleta bo możemy sami wybrać czy kopiujemy obiekt z dziurami czy bez- to jak duże są dane dziury i gdzie są rozmieszczone zależy od typów zmiennych i kolejności ich deklaracji w strukturze, z mojego wieloletniego doświadczenia wynika, że najwięcej miejsca możemy uzyskać jeśli będziemy deklarować zmienne od najmniejszej do największej i uważam, że jest to najlepsza programistyczna praktyka
Kończąc ten przydługawy wpis życzę wam miłego wsadzania danych w dziury i możecie dać w komentarzach co tam pochowaliście przed użytkownikami w swoich strukturach (✌ ゚ ∀ ゚)☞
#programowanie #cpp #cplusplus
źródło: comment_1650787236o72Kmv50IWaFFngZckbGsF.jpg
PobierzKomentarz usunięty przez autora
I dlaczego nie działa kiedy zrobimy (int)(&obj.zmiennaChar+2)?
Co więcej standard języka C wprost mówi, że:
źródło: comment_1650790233H0pt5jtmtsH6vOqqbQcehy.jpg
Pobierzźródło: comment_16507906500QOXJMvHrwgO3ch3EC37Cy.jpg
PobierzKomentarz usunięty przez autora
Korzystam czesto przy strukturach
Wpisz swojego structa i dodaj:
char a