Jedną rzeczą, która zawsze intuicyjnie wydawała mi się pozytywną cechą C (a właściwie jej implementacji, takich jak gcc, clang, ...), jest to, że nie przechowuje żadnych ukrytych informacji obok twoich zmiennych w czasie wykonywania. Rozumiem przez to, że jeśli na przykład chciałbyś mieć zmienną „x” typu „uint16_t”, możesz być pewien, że „x” zajmie tylko 2 bajty miejsca (i nie przeniesie żadnych ukrytych informacji, takich jak jej typ itp. .). Podobnie, jeśli chcesz tablicę 100 liczb całkowitych, możesz być pewien, że jest ona tak duża jak 100 liczb całkowitych.
Jednak im bardziej próbuję wymyślić konkretne przypadki użycia tej funkcji, tym bardziej zastanawiam się, czy rzeczywiście ma ona jakieś praktyczne zalety. Jedyne, co do tej pory mogłem wymyślić, to to, że oczywiście potrzebuje mniej pamięci RAM. W przypadku ograniczonych środowisk, takich jak układy AVR itp., Jest to zdecydowanie ogromny plus, ale w codziennych przypadkach użytkowania komputerów stacjonarnych / serwerów wydaje się to raczej nieistotne. Inną możliwością, o której myślę, może być pomocna / kluczowa dla uzyskania dostępu do sprzętu, a może mapowania regionów pamięci (na przykład dla wyjścia VGA i tym podobnych) ...?
Moje pytanie: Czy są jakieś konkretne domeny, które albo nie mogą, albo mogą być bardzo niewygodne do wdrożenia bez tej funkcji?
PS Powiedz mi, czy masz na to lepszą nazwę! ;)
źródło
virtual
członka. Tak więc RTTI nigdy nie zwiększa rozmiaru żadnych obiektów, tylko powiększa plik binarny o stałą.T *
znaczy, zawsze ma ten sam rozmiar iT
może zawierać ukryte pole wskazujące na vtable. A nie kompilator C ++ kiedykolwiek wprowadzony vtables do obiektów, które ich nie potrzebują.Odpowiedzi:
Istnieje kilka korzyści, z których oczywistą jest, że w czasie kompilacji parametry takie jak parametry funkcji pasują do przekazywanych wartości.
Ale myślę, że pytasz o to, co dzieje się w czasie wykonywania.
Należy pamiętać, że kompilator utworzy środowisko wykonawcze, które osadzi wiedzę o typach danych w wykonywanych operacjach. Każdy fragment danych w pamięci może nie być samoopisujący, ale kod z natury wie, co to są dane (jeśli poprawnie wykonałeś swoją pracę).
W czasie wykonywania rzeczy są nieco inne niż myślisz.
Na przykład nie zakładaj, że podczas deklaracji uint16_t używane są tylko dwa bajty. W zależności od procesora i wyrównania słów może on zajmować 16, 32 lub 64 bity na stosie. Może się okazać, że Twój zestaw szortów zużywa znacznie więcej pamięci, niż się spodziewałeś.
Może to być problematyczne w niektórych sytuacjach, w których należy odwoływać się do danych przy określonych przesunięciach. Dzieje się tak podczas komunikacji między dwoma systemami, które mają różne architektury procesorów, za pośrednictwem łącza bezprzewodowego lub plików.
C umożliwia określenie struktur o ziarnistości na poziomie bitów:
Ta struktura ma trzy bajty, z krótkim zdefiniowanym, aby rozpocząć od nieparzystego przesunięcia. Będzie również musiał zostać zapakowany, aby był dokładnie taki, jak zdefiniowałeś. W przeciwnym razie kompilator wyrówna słowa.
Kompilator wygeneruje kod za kulisami, aby wyodrębnić te dane i skopiować do rejestru, abyś mógł robić z nim użyteczne rzeczy.
Teraz możesz zobaczyć, że za każdym razem, gdy mój program uzyskuje dostęp do członka struktury myMessage, będzie wiedział, jak dokładnie go wyodrębnić i na nim operować.
Może to stać się problematyczne i trudne w zarządzaniu podczas komunikacji między różnymi systemami z różnymi wersjami oprogramowania. Musisz dokładnie zaprojektować system i kod, aby zapewnić, że obie strony mają dokładnie taką samą definicję typów danych. W niektórych środowiskach może to być dość trudne. Tutaj potrzebujesz lepszego protokołu, który zawiera dane samoopisujące, takie jak Bufory protokołów Google .
Na koniec warto zadać sobie pytanie, jak ważne jest to w środowisku pulpitu / serwera. To zależy od tego, ile pamięci planujesz użyć. Jeśli wykonujesz coś takiego jak przetwarzanie obrazu, możesz skończyć z użyciem dużej ilości pamięci, co może wpłynąć na wydajność aplikacji. Jest to z pewnością zawsze problem w środowisku osadzonym, w którym pamięć jest ograniczona i nie ma pamięci wirtualnej.
źródło
short
. Jest to jednak jednorazowe wymaganie dotyczące początku tablicy, reszta jest automatycznie dopasowywana poprawnie ze względu na to, że jest kolejna.uint8_t padding: 6;
, podobnie jak pierwsze dwa bity. Lub, bardziej precyzyjnie, tylko komentarz//6 bits of padding inserted by the compiler
. Struktura, jak ją napisałeś, ma rozmiar co najmniej dziewięciu bajtów, a nie trzech.Trafiłeś na jeden z jedynych powodów, dla których jest to przydatne: mapowanie zewnętrznych struktur danych. Należą do nich bufory wideo odwzorowane w pamięci, rejestry sprzętowe itp. Obejmują one również dane przesyłane w stanie nienaruszonym poza programem, takie jak certyfikaty SSL, pakiety IP, obrazy JPEG i prawie każda inna struktura danych, która ma trwałe życie poza programem.
źródło
C jest językiem niskiego poziomu, prawie przenośnym asemblerem, więc jego struktury danych i konstrukcje językowe są zbliżone do metalu (struktury danych nie mają ukrytych kosztów - oprócz wypełnienia, wyrównania i ograniczeń wielkości narzuconych przez sprzęt i ABI ). Tak więc C rzeczywiście nie ma natywnego pisania dynamicznego. Ale jeśli potrzebujesz, możesz przyjąć konwencję, że wszystkie twoje wartości są agregatami, zaczynając od pewnych informacji o typie (np. Niektóre
enum
...); Zastosowanieunion
-s i (w przypadku, tablicowej przedmiotów) elastycznego członu matrycy nastruct
zawierającego również rozmiar tablicy.(programując w C, Twoim obowiązkiem jest zdefiniowanie, udokumentowanie i przestrzeganie użytecznych konwencji - w szczególności warunków wstępnych i dodatkowych oraz niezmienników; również dynamiczna alokacja pamięci C wymaga wyjaśnienia konwencji, kto powinien mieć strefę pamięci o
free
dużymmalloc
zasięgu)Tak więc, aby przedstawić wartości, które są liczbami całkowitymi w ramkach, ciągami znaków lub jakimś symbolem podobnym do schematu lub wektorem wartości, koncepcyjnie użyjesz oznaczonego połączenia (zaimplementowanego jako połączenie wskaźników) - zawsze zaczynając od rodzaju -, np .:
Aby uzyskać dynamiczny typ jakiejś wartości
Oto „dynamiczny rzut” na wektory:
oraz „bezpieczny akcesorium” wewnątrz wektorów:
Zazwyczaj zdefiniujesz większość krótkich funkcji powyżej, jak
static inline
w niektórych plikach nagłówkowych.BTW, jeśli możesz użyć zbieracza śmieci Boehm'a, możesz dość łatwo kodować w stylu wyższego poziomu (ale niebezpiecznym), a kilka interpretatorów schematów jest wykonywanych w ten sposób. Wariacyjny konstruktor wektorowy może być
a jeśli masz trzy zmienne
możesz zbudować z nich wektor za pomocą
make_vector(3,v1,v2,v3)
Jeśli nie chcesz używać zbieracza śmieci Boehm'a (lub zaprojektować swój własny), powinieneś bardzo ostrożnie definiować destruktory i dokumentować, kto, jak i kiedy pamięć powinna być
free
-d; zobacz ten przykład. Możesz więc użyćmalloc
(ale następnie przetestować podGC_MALLOC
kątem jego awarii) zamiast powyższego, ale musisz dokładnie zdefiniować i użyć funkcji destruktoravoid destroy_value(value_t)
Siła C ma być na niskim poziomie, aby umożliwić kod jak wyżej i zdefiniować własne konwencje (szczególnie dla twojego oprogramowania).
źródło