Długo długo w c99

Jan 10 2021

W standardzie C99 wprowadzili long long. Jaki jest tego cel? W moim (ograniczonym) doświadczeniu w programowaniu w C widziałem tylko 4-bajtowe int i 8-bajtowe. Na przykład z Compiler Explorer:

Jeśli longjuż jest 8, dlaczego konieczne jest dodanie innego long longtypu? Co to robi z kompilatorem / architekturą?

Odpowiedzi

6 chux-ReinstateMonica Jan 10 2021 at 05:36

Jeśli długa to już 8, to dlaczego konieczne jest dodanie kolejnego długiego typu? Co to robi z kompilatorem / architekturą?

„Jeśli long jest już 8” nie zawsze jest prawdą, ponieważ istnieje wiele kodu opartego na 32-bitach longi intna 32 lub 16 bitach.

Wymaganie longjako 64-bitowe złamałoby podstawy kodu. To jest poważny problem.


Jednak wymóg longpozostania 32-bitowym (i nie long long) nie zapewniłby dostępu do standardowych 64-bitowych liczb całkowitych, stąd uzasadnienie dla long long.

Zezwolenie longna 32-bitowe lub 64-bitowe (lub inne) umożliwia przejście.

Różne funkcje przechodzą / powracają, longjak fseek(), ftell(). Korzystają z tego, longże są ponad 32-bitowe do obsługi dużych plików.

Zalecana praktyka zachęca do szerszego long: „Typy używane dla size_ti ptrdiff_tnie powinny mieć rangi konwersji liczby całkowitej większej niż ranga konwersji z, signed long intchyba że implementacja obsługuje obiekty wystarczająco duże, aby było to konieczne.” Odnosi się to do rozmiarów pamięci przekraczających 32 bity.


Być może w przyszłości implementacja może używać int/long/long long/intmax_tbitów 32/64/128/256.

IAC, widzę stałej szerokości typów intN_tcoraz popularniejsze nad longi long long. Zwykle używam typów o stałej szerokości lub bool, ( unsigned) char, int/ unsigned, size_t( u) intmax_ti Leave signed char, ( unsigned) short, ( unsigned) long, ( unsigned) long longw szczególnych przypadkach.

4 dbush Jan 10 2021 at 05:26

Standard C gwarantuje tylko, że an intmoże mieć (luźno mówiąc) 2 bajty, a longmoże mieć 4 bajty, a a long longmoże mieć 8 bajtów.

W rzeczywistości MSVC nadal używa 4 bajtów, longmimo że ma 4 bajty int.

3 NateEldredge Jan 10 2021 at 05:38

Jedynym istotnym wymaganiem dla inti long, wtedy i teraz, jest to, że intmusi mieć co najmniej 16 bitów i longco najmniej 32 bity. Systemy 16- i 32-bitowe zwykle mają 32-bitowe long, a 64-bitowe komputery były znacznie mniej powszechne pod koniec lat 90-tych. Tak więc przed C99 programiści nie mogli w przenośny polegać na dostępnym w ogóle 64-bitowym typie liczb całkowitych. Ten problem został rozwiązany przez wprowadzenie long long, które musi mieć co najmniej 64 bity. (Myślę, że zostało już dostarczone przez GCC i być może inne kompilatory jako rozszerzenie).

W dzisiejszych czasach wiele (ale nie wszystkie) systemów 64-bitowych używa 64-bitowego longi nie zawraca sobie głowy long longpowiększaniem, więc jest to również 64-bitowe i jest w pewnym sensie redundantne. Są to prawdopodobnie systemy, które znasz, ale nie reprezentują one wszystkiego.

2 PeterCordes Jan 10 2021 at 10:29

Myślę, że nie zdawałeś sobie sprawy, że robisz ogromne, błędne założenie o tym, jak działają wymagania dotyczące szerokości typu C: ISO C ustawia tylko minimalny zakres wartości, taki jak najmniejsza dozwolona wielkość LONG_MAXi LONG_MIN(-2147483647, a nie 8, ponieważ ISO C pozwala na dopełnienie i liczby całkowite ze znakiem / wielkością ze znakiem, a nie tylko dopełnienie do 2). Rzeczywiste implementacje mogą mieć szersze typy, często w celu dopasowania szerokości rejestru lub rozmiaru operandu, które maszyna docelowa może wydajnie wykonać.

Wiele napisano o tym na Stack Overflow i gdzie indziej, czego nie zamierzam tutaj powtarzać. Zobacz teżhttps://en.cppreference.com/w/c/language/arithmetic_types


To doprowadziło cię do błędu polegającego na spojrzeniu na opcje szerokości typu w ABI x86-64 System V i założeniu, że inne implementacje C są takie same, jak sądzę. x86-64 to 64-bitowy ISA, który może wydajnie pracować z 64-bitowymi liczbami całkowitymi, więc 64-bitowy longbył dość rozsądnym wyborem.

Żadne rozsądne ABI dla 32-bitowej maszyny, takiej jak i386, nie używałoby 64-bitowego, longponieważ nie jest to wymagane, tylko 32-bitowe. Używanie 64-bitowego oznaczałoby, że nie mieści się w jednym rejestrze. Kompiluj z -m32lub kompiluj dla 32-bitowego ARM. Godbolt ma również GCC dla AVR i MSP430. Na maszynach 8-bitowych i 16-bitowych GCC wybiera najmniejsze szerokości dozwolone przez ISO C (2-bajtowe intitd.)

W 1999 roku x86-64 nawet nie istniał. (Kilka innych 64-bitowych ISA tak zrobiło, jak Alpha). Więc patrząc na jeden z 2 głównych ABI, aby zrozumieć wybory C99, nie zaprowadzi cię zbyt daleko.

Oczywiście C potrzebuje typu, który jest co najmniej 64-bitowy, aby umożliwić ludziom pisanie programów, które wydajnie wykonują 64-bitowe obliczenia całkowitoliczbowe.


A tak przy okazji, x86-64 może robić 32-bitowe liczby całkowite równie wydajnie, jak 64-bit, a czasem bardziej wydajnie. Tak więc tworzenie longtypu 64-bitowego prawdopodobnie nie jest świetne. Niektóre kody używają, longponieważ chcą typu, który musi być 32-bitowy, ale nie ma korzyści z posiadania go szerszego. W przypadku takiego kodu 64-bitowy longpo prostu marnuje rozmiar pamięci podręcznej / przepustowość pamięci i rozmiar kodu (prefiksy REX). W C99 byłby idealny wybór int_least32_t, ale jest to irytująco długie i rzadko używane.

Czasami jednak oczekuje się, że OTOH longbędzie „najbardziej wydajnym (1-rejestrowym) typem”, chociaż nie ma takiej gwarancji, a interfejsy ABI LLP64, takie jak Windows x64 z 32-bitową wersją, longnie są takie.

Inną całą puszką robaków jest C99 int_fast32_ti x86-64 System V, który IMO wybrał kiepski wybór, aby uczynić ten typ 64-bitowym. (Mam w połowie napisaną odpowiedź dla Cpp uint32_fast_t jest tłumaczona na uint64_t, ale jest wolniejsza dla prawie wszystkich operacji niż uint32_t (x86_64). Dlaczego rozwiązuje się z uint64_t? Który powinienem skończyć ... pojawiaint_fast32_t się pytanie „szybko po co celu ”, aw wielu implementacjach nie jest to coś, czego można by oczekiwać w wielu przypadkach.

Zobacz też

  • C ++ - najszybszy typ liczby całkowitej?
  • W jaki sposób należy zdefiniować typy [u] int_fastN_t dla x86_64, z lub bez ABI x32?
  • Dlaczego uint32_t miałby być preferowany zamiast uint_fast32_t?
  • Dlaczego uint_least16_t jest szybszy niż uint_fast16_t do mnożenia w x86_64?
  • Optymalizacje kompilatora dozwolone za pośrednictwem typów „int”, „najmniej” i „szybkich” o stałej szerokości C / C ++
old_timer Jan 11 2021 at 06:16

Istnieją pewne ograniczenia, ale autor kompilatora ma swobodę wyboru długości dla standardowych typów zmiennych C (char, short, int, long, long long). Naturalnie char będzie bajtem dla tej architektury (większość kompilatorów C ma 8 bitów). I oczywiście nie można mieć czegoś mniejszego, większego niż coś większego, long nie może być mniejsze niż int. Ale z pewnością do 1999 roku widzieliśmy przejście x86 z 16 na 32 bitów i na przykład int zmienił się z 16 na 32 za pomocą wielu narzędzi, ale długo pozostał 32. Później nastąpiło przejście z 32 na 64 bitowe x86 iw zależności od narzędzia były dostępne typy pomóc.

Problem istniał na długo przed tym, a rozwiązaniem nie było ustalanie długości typów, są one, w ramach reguł, zależne od autorów kompilatora co do rozmiaru. Ale autorzy kompilatora muszą stworzyć plik stdint.h, który pasuje do narzędzia i celu (stdint.h jest specyficzny dla narzędzia i docelowy co najmniej i może być wersją narzędzia i opcjami kompilacji dla tego narzędzia itp.). Na przykład uint32_t ma zawsze 32 bity. Niektórzy autorzy przekonwertują to na int, inni long, itp. W swoim stdint.h. Typy zmiennych języka C pozostają ograniczone do typu char, short, int itp. W zależności od języka (uint32_t nie jest typem zmiennej, jest konwertowany na typ zmiennej przez stdint.h). To rozwiązanie / obejście było sposobem na uniknięcie szaleństwa i utrzymanie języka przy życiu.

Autorzy często wybierają, na przykład, czy GPR są 16-bitowe, aby int będzie 16-bitowy, a jeśli 32-bitowy będzie 32-bitowy i tak dalej, ale mają pewną swobodę.

Tak, oznacza to w szczególności, że nie ma powodu, aby zakładać, że dowolne dwa narzędzia dla określonego celu (na przykład komputera, na którym to czytasz) używają tych samych definicji w szczególności dla int i long, a jeśli chcesz napisać kod dla ta platforma, która może przenosić się przez te narzędzia (obsługujące tę platformę), a następnie używać typów stdint.h, a nie int, long, itp ... Z pewnością jeśli przechodzisz przez platformy msp430 mcu, ramię MCU, ramię linux , maszyna oparta na x86, której typy, nawet dla tego samego "toolchaina" (na przykład gnu gcc i binutils), nie mają takich samych definicji int i long, itp. char i short mają zwykle 8 i 16 bitów, int i long mają tendencję do zmieniania się najbardziej, czasem te same rozmiary, czasami różne, ale nie chodzi o to, aby zakładać.

Wykrywanie rozmiarów, wersji kompilatora / celu / opcji wiersza poleceń lub po prostu przejście standardowej trasy, aby zminimalizować problemy później, jest trywialne.