Aktywne Wpisy
USER_303 0
Wszystkie znaki na niebie wskazują że mój brat jest ćpunem i niszczy sobie życie ... Ja nawet nie wiem co robić, nie da się z nim porozmawiać nawet ... Rozwiązaniem które coś by zmieniło to zgłosić na policję, ale nie chcę mu tego robić, może jeszcze da się coś od niego dowiedzieć, pomóc mu. Jakieś propozycje jak z nim porozmawiać ?
#pytanie #narkotykizawszespoko
#pytanie #narkotykizawszespoko
G06DbT +14
Dziś znowu wy będziecie siedzieć w klimatyzowanych biurach i mieszkaniach, na swoich wygodnych fotelach czy sofach, pijący dobrą kawę, pachnący i w czystych ubraniach.
A ja? Ja się obudziłem o 3:27 (budzik na 4 był nastawiony), raz zwlekę się z barłogu żeby sobie zrobić śniadanie i żarcie do kołchozu. Po godzinie 5 pojadę, i znowu będę musiał się przebrać w ciuchy robocze, trzeba będzie odbić kartę i kołchozowanie będzie wykonywane.
Szkoda ze
A ja? Ja się obudziłem o 3:27 (budzik na 4 był nastawiony), raz zwlekę się z barłogu żeby sobie zrobić śniadanie i żarcie do kołchozu. Po godzinie 5 pojadę, i znowu będę musiał się przebrać w ciuchy robocze, trzeba będzie odbić kartę i kołchozowanie będzie wykonywane.
Szkoda ze
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
Komentarz 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:
Komentarz usunięty przez autora
Korzystam czesto przy strukturach
Wpisz swojego structa i dodaj:
char a : 8
int c : 24 // skomentuj tą linijke i powinienes zmienic rozmiar structury
int b : 32