Czy istnieje sposób na użycie sizeof
makra preprocesora?
Na przykład przez lata było mnóstwo sytuacji, w których chciałem zrobić coś takiego:
#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif
Dokładna rzecz, którą tutaj sprawdzam, jest całkowicie wymyślona - chodzi o to, że często lubię wprowadzać tego typu kontrole (rozmiar lub wyrównanie) w czasie kompilacji, aby chronić przed kimś modyfikującym strukturę danych, która mogłaby źle dopasować lub zmienić rozmiar rzeczy, które mogłyby je złamać.
Nie trzeba dodawać - nie wydaje mi się, żebym mógł używać sizeof
w sposób opisany powyżej.
Odpowiedzi:
Można to zrobić na kilka sposobów. Następujące fragmenty nie wygenerują żadnego kodu, jeśli są
sizeof(someThing)
równePAGE_SIZE
; w przeciwnym razie spowodują błąd w czasie kompilacji.1. C11 sposób
Począwszy od C11 możesz użyć
static_assert
(wymagania#include <assert.h>
).Stosowanie:
static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");
2. Makro niestandardowe
Jeśli chcesz po prostu otrzymać błąd kompilacji, który
sizeof(something)
nie jest tym, czego oczekujesz, możesz użyć następującego makra:#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
Stosowanie:
BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );
W tym artykule szczegółowo wyjaśniono, dlaczego to działa.
3. Specyficzne dla MS
Na kompilatorze Microsoft C ++ możesz użyć makra C_ASSERT (wymaga
#include <windows.h>
), które wykorzystuje sztuczkę podobną do tej opisanej w sekcji 2.Stosowanie:
C_ASSERT(sizeof(someThing) == PAGE_SIZE);
źródło
gcc
systemie GNU (testowane w wersji 4.8.4) (Linux). Na((void)sizeof(...
to błędy zexpected identifier or '(' before 'void'
iexpected ')' before 'sizeof'
. Ale w zasadziesize_t x = (sizeof(...
działa zgodnie z przeznaczeniem. Trzeba jakoś „wykorzystać” wynik. Aby umożliwić wielokrotne wywoływanie tego w funkcji lub w zasięgu globalnym, coś takiegoextern char _BUILD_BUG_ON_ [ (sizeof(...) ];
może być używane wielokrotnie (bez skutków ubocznych,_BUILD_BUG_ON_
nigdzie nie odwołuj się).Nie. Dyrektywy warunkowe przyjmują ograniczony zestaw wyrażeń warunkowych;
sizeof
jest jedną z niedozwolonych rzeczy.Dyrektywy przetwarzania wstępnego są oceniane przed przeanalizowaniem źródła (przynajmniej koncepcyjnie), więc nie ma jeszcze żadnych typów ani zmiennych, aby uzyskać ich rozmiar.
Istnieją jednak techniki uzyskiwania asercji w czasie kompilacji w języku C (na przykład zobacz tę stronę ).
źródło
Wiem, że to późna odpowiedź, ale aby dodać do wersji Mike'a, oto wersja, której używamy, która nie przydziela żadnej pamięci. Nie wymyśliłem oryginalnego sprawdzenia rozmiaru, znalazłem go w Internecie lata temu i niestety nie mogę się odwołać do autora. Pozostałe dwa to tylko rozszerzenia tego samego pomysłu.
Ponieważ są typedef, nic nie jest przydzielane. Z __LINE__ w nazwie, zawsze jest to inna nazwa, więc można ją skopiować i wkleić w razie potrzeby. Działa to w kompilatorach MS Visual Studio C i kompilatorach GCC Arm. Nie działa w CodeWarrior, CW narzeka na redefinicję, nie korzysta z konstrukcji preprocesora __LINE__.
//Check overall structure size typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1]; //check 8 byte alignment for flash write or similar typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1]; //check offset in structure to ensure a piece didn't move typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];
źródło
#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
Wiem, że ten wątek jest naprawdę stary, ale ...
Moje rozwiązanie:
extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];
Dopóki to wyrażenie jest równe zero, kompiluje się dobrze. Cokolwiek innego i to właśnie tam wybucha. Ponieważ zmienna jest umieszczona na zewnątrz, nie zajmie miejsca i dopóki nikt się do niej nie odwoła (a tego nie zrobi), nie spowoduje to błędu łącza.
Nie tak elastyczne jak makro assert, ale nie mogłem go skompilować w mojej wersji GCC, a to ... i myślę, że skompiluje się prawie wszędzie.
źródło
Istniejące odpowiedzi pokazują tylko, jak osiągnąć efekt „asercji w czasie kompilacji” w oparciu o rozmiar typu. Może to zaspokoić potrzeby PO w tym konkretnym przypadku, ale są inne przypadki, w których naprawdę potrzebujesz preprocesora warunkowego w oparciu o rozmiar typu. Oto jak to zrobić:
Napisz sobie mały program w C, taki jak:
/* you could call this sizeof_int.c if you like... */ #include <stdio.h> /* 'int' is just an example, it could be any other type */ int main(void) { printf("%zd", sizeof(int); }
Skompiluj to. Napisz skrypt w swoim ulubionym języku skryptowym, który uruchamia powyższy program w C i przechwytuje jego dane wyjściowe. Użyj tego wyjścia, aby wygenerować plik nagłówkowy C. Na przykład, jeśli używasz Rubiego, może to wyglądać tak:
sizeof_int = `./sizeof_int` File.open('include/sizes.h','w') { |f| f.write(<<HEADER) } /* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */ #define SIZEOF_INT #{sizeof_int} /* others can go here... */ HEADER
Następnie dodaj regułę do swojego Makefile lub innego skryptu budującego, co spowoduje uruchomienie powyższego skryptu w celu zbudowania
sizes.h
.Uwzględnij
sizes.h
wszędzie tam, gdzie potrzebujesz użyć warunków warunkowych preprocesora na podstawie rozmiarów.Gotowe!
(Czy kiedykolwiek pisałeś,
./configure && make
aby zbudować program? Jakieconfigure
skrypty robią, jest w zasadzie takie samo jak powyższe ...)źródło
A co z następnym makrem:
/* * Simple compile time assertion. * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes); */ #define CT_ASSERT(exp, message_identifier) \ struct compile_time_assertion { \ char message_identifier : 8 + !(exp); \ }
Na przykład w komentarzu MSVC mówi coś takiego:
test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits
źródło
#if
dyrektywie preprocesora.Jako odniesienie do tej dyskusji, zgłaszam, że niektóre kompilatory uzyskują sizeof () ar czasu preprocesora.
Odpowiedź Jamesa McNellisa jest poprawna, ale niektórzy kompilatorzy przechodzą przez to ograniczenie (prawdopodobnie narusza to ścisłą ansi c).
W tym przypadku odnoszę się do kompilatora IAR C (prawdopodobnie wiodącego dla profesjonalnego mikrokontrolera / programowania wbudowanego).
źródło
sizeof
w czasie wstępnego przetwarzania.sizeof
należy traktować jako zwykły identyfikator.#if (sizeof(int) == 8)
faktycznie działały (na niektórych kompilatorach)”. Odpowiedź: „To musiało być przed moimi czasami”, pochodziła od Dennisa Ritchiego.#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x))
może działaćźródło
#define SIZEOF_TYPE(x) (((x*)0) + 1)
#if
warunku. Nie zapewnia żadnych korzyścisizeof(x)
.W C11
_Static_assert
dodano słowo kluczowe. Może być używany jako:_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")
źródło
W moim przenośnym kodzie C ++ ( http://www.starmessagesoftware.com/cpcclibrary/ ) chciałem zabezpieczyć rozmiary niektórych moich struktur lub klas.
Zamiast znaleźć sposób, w jaki preprocesor zgłosi błąd (który nie może działać z sizeof (), jak podano tutaj), znalazłem tutaj rozwiązanie, które powoduje, że kompilator zgłasza błąd. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99
Musiałem dostosować ten kod, aby generował błąd w moim kompilatorze (xcode):
static union { char int8_t_incorrect[sizeof( int8_t) == 1 ? 1: -1]; char uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1]; char int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1]; char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1]; char int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1]; char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1]; };
źródło
Po wypróbowaniu wspomnianych makr ten fragment wydaje się dawać pożądany rezultat (
t.h
):#include <sys/cdefs.h> #define STATIC_ASSERT(condition) typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1] STATIC_ASSERT(sizeof(int) == 4); STATIC_ASSERT(sizeof(int) == 42);
Bieganie
cc -E t.h
:# 1 "t.h" ... # 2 "t.h" 2 typedef char _static_assert_3[ (sizeof(int) == 4) ? 1 : -1]; typedef char _static_assert_4[ (sizeof(int) == 42) ? 1 : -1];
Bieganie
cc -o t.o t.h
:% cc -o t.o t.h t.h:4:1: error: '_static_assert_4' declared as an array with a negative size STATIC_ASSERT(sizeof(int) == 42); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ t.h:2:84: note: expanded from macro 'STATIC_ASSERT' ...typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1] ^~~~~~~~~~~~~~~~~~~~ 1 error generated.
42 nie jest jednak odpowiedzią na wszystko ...
źródło
Aby sprawdzić w czasie kompilacji rozmiar struktur danych pod kątem ich ograniczeń, użyłem tej sztuczki.
#if defined(__GNUC__) { char c1[sizeof(x)-MAX_SIZEOF_X-1]; } // brakets limit c1's scope #else { char c1[sizeof(x)-MAX_SIZEOF_X]; } #endif
Jeśli rozmiar x jest większy lub równy limitowi MAX_SIZEOF_X, to gcc będzie narzekać z błędem „rozmiar tablicy jest za duży”. VC ++ wygeneruje błąd C2148 („całkowity rozmiar tablicy nie może przekraczać 0x7fffffff bajtów”) lub C4266 „nie może przydzielić tablicy o stałym rozmiarze 0”.
Te dwie definicje są potrzebne, ponieważ gcc pozwoli na zdefiniowanie w ten sposób tablicy o rozmiarze zerowym (sizeof x - n).
źródło
sizeof
Operator nie jest dostępny dla preprocesora, ale można przenieśćsizeof
do kompilatora i sprawdzenie stanu w czasie pracy:#define elem_t double #define compiler_size(x) sizeof(x) elem_t n; if (compiler_size(elem_t) == sizeof(int)) { printf("%d",(int)n); } else { printf("%lf",(double)n); }
źródło
compiler_size
służy definiowanie ? Co twój przykład próbuje pokazać?