W kodzie jądra Linuksa znalazłem następującą rzecz, której nie mogę zrozumieć.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Kod jest tutaj: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Jaka jest potrzeba i cel tablicy danych bez elementów?
c
structure
flexible-array-member
Jeegar Patel
źródło
źródło
Odpowiedzi:
Jest to sposób na posiadanie zmiennych rozmiarów danych bez konieczności dwukrotnego wywoływania
malloc
(kmalloc
w tym przypadku). Używałbyś tego w ten sposób:Kiedyś to nie było standardowe i zostało uznane za włamanie (jak powiedział Aniket), ale tak było ustandaryzowane w C99 . Standardowy format to teraz:
Zwróć uwagę, że nie wspominasz o żadnym rozmiarze dla
data
pola. Zauważ również, że ta specjalna zmienna może pojawić się tylko na końcu struktury.W C99 ta sprawa jest wyjaśniona w 6.7.2.1.16 (moje wyróżnienie):
Innymi słowy, jeśli masz:
Możesz uzyskać dostęp
var->data
do indeksów w formacie[0, extra)
. Zauważ, żesizeof(struct something)
poda rozmiar uwzględniający tylko inne zmienne, tj. Dajedata
rozmiar 0.Ciekawe może być również odnotowanie, w jaki sposób norma faktycznie podaje przykłady
malloc
takiej konstrukcji (6.7.2.1.17):Kolejną interesującą notą według standardu w tej samej lokalizacji jest (wyróżnienie moje):
źródło
[0, extra)
?W rzeczywistości jest to hack dla GCC ( C90 ).
Nazywa się to również hackowaniem struktury .
Więc następnym razem powiedziałbym:
Będzie to równoznaczne z powiedzeniem:
Mogę stworzyć dowolną liczbę takich obiektów strukturalnych.
źródło
Chodzi o to, aby umożliwić umieszczenie tablicy o zmiennej wielkości na końcu struktury. Przypuszczalnie
bts_action
jest to pakiet danych z nagłówkiem o stałym rozmiarze ( polatype
isize
) i składową o zmiennej wielkościdata
. Deklarując ją jako tablicę o długości 0, można ją indeksować tak jak każdą inną tablicę. Następnie przydzieliłbyśbts_action
strukturę, powiedzmy odata
rozmiarze 1024 bajtów , na przykład:Zobacz także: http://c2.com/cgi/wiki?StructHack
źródło
malloc
sprawia, że wielokrotnie się powtarzasz i jeśli kiedykolwiek rodzajaction
zmian, to musisz to wielokrotnie poprawiać. Porównaj dla siebie poniższe dwa, a będziesz wiedział:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
Drugi jest krótszy, czystszy i wyraźnie łatwiejszy do zmiany.Kod jest nieprawidłowy C ( zobacz to ). Jądro Linuksa, z oczywistych powodów, nie przejmuje się w najmniejszym stopniu przenośnością, więc wykorzystuje mnóstwo niestandardowego kodu.
To, co robią, to niestandardowe rozszerzenie GCC o rozmiarze tablicy 0. Napisałby program zgodny ze standardem
u8 data[];
i oznaczałoby to dokładnie to samo. Autorzy jądra Linuksa najwyraźniej uwielbiają robić rzeczy niepotrzebnie skomplikowane i niestandardowe, jeśli pojawi się taka opcja.W starszych standardach C kończenie struktury pustą tablicą było nazywane „hackowaniem struktury”. Inni już wyjaśnili jego cel w innych odpowiedziach. Struct hack, w standardzie C90, był niezdefiniowanym zachowaniem i może powodować awarie, głównie dlatego, że kompilator C może dodać dowolną liczbę bajtów wypełniających na końcu struktury. Takie bajty wypełniające mogą kolidować z danymi, które próbowałeś „włamać” na końcu struktury.
GCC wcześnie wprowadziło niestandardowe rozszerzenie, aby zmienić to zachowanie z niezdefiniowanego na dobrze zdefiniowane. Standard C99 dostosował następnie tę koncepcję i każdy nowoczesny program w języku C może więc bez ryzyka korzystać z tej funkcji. Jest znany jako elastyczny element tablicy w C99 / C11.
źródło
Innym zastosowaniem tablicy o zerowej długości jest nazwana etykieta wewnątrz struktury, aby pomóc w sprawdzaniu przesunięcia struktury w czasie kompilacji.
Załóżmy, że masz kilka dużych definicji struktur (obejmujących wiele linii pamięci podręcznej), które chcesz mieć pewność, że są wyrównane do granicy linii pamięci podręcznej zarówno na początku, jak iw środku, gdzie przecina granicę.
W kodzie możesz zadeklarować je używając rozszerzeń GCC takich jak:
Ale nadal chcesz się upewnić, że jest to wymuszane w czasie wykonywania.
To działałoby dla pojedynczej struktury, ale byłoby trudno objąć wiele struktur, z których każda ma inną nazwę elementu członkowskiego do wyrównania. Najprawdopodobniej otrzymasz kod podobny do poniższego, w którym musisz znaleźć nazwy pierwszego członka każdej struktury:
Zamiast iść w ten sposób, możesz zadeklarować w strukturze tablicę o zerowej długości, działającą jako nazwana etykieta ze spójną nazwą, ale nie zajmującą żadnej spacji.
Wtedy kod asercji środowiska uruchomieniowego byłby znacznie łatwiejszy w utrzymaniu:
źródło