długo w C / C ++

84

Próbuję tego kodu na kompilatorze GNU C ++ i nie jestem w stanie zrozumieć jego zachowania:

#include <stdio.h>;

int main()
{
    int  num1 = 1000000000;
    long num2 = 1000000000;
    long long num3;
    //num3 = 100000000000;
    long long num4 = ~0;

    printf("%u %u %u", sizeof(num1), sizeof(num2), sizeof(num3));
    printf("%d %ld %lld %llu", num1, num2, num3, num4);
    return 0;
}

Kiedy odkomentuję skomentowaną linię, kod nie kompiluje się i wyświetla błąd:

błąd: stała całkowita jest za duża dla typu długiego

Ale jeśli kod zostanie skompilowany tak, jak jest i jest wykonywany, generuje wartości znacznie większe niż 10000000000.

Czemu?

sud03r
źródło
8
Może być już za późno, ale przyszłym czytelnikom sugeruję użycie <stdint.h>i użycie uint64_t. Aby wyświetlić wartość 64-bitową,printf( "%" PRIu64 "\n", val);
entuzjasticgeek
Uwzględniono @enthusiasticgeek <stdint.h>,uint64_t a = 0xffffffffffffff; printf( "%" PRIu64 "\n",a ); : error: expected ‘)’ before ‘PRIu64’ printf( "%" PRIu64 "\n",a ); :: warning: spurious trailing ‘%’ in format [-Wformat=] printf( "%" PRIu64 "\n",a );
Herdsman

Odpowiedzi:

147

Litery 100000000000 tworzą literalną stałą całkowitą, ale wartość jest zbyt duża dla typu int. Musisz użyć sufiksu, aby zmienić typ literału, tj

long long num3 = 100000000000LL;

Przyrostek LLprzekształca literał w typ long long. C nie jest wystarczająco „inteligentny”, aby wywnioskować to na podstawie typu po lewej stronie, typ jest własnością samego literału, a nie kontekstu, w którym jest używany.

rozwijać
źródło
47
Powrót kiedy ta odpowiedź została napisana było prawdopodobnie poprawne, ale teraz standard C ++ mówi, że typ w dosłownym całkowitej bez przyrostka jest pierwszym int, long inti long long intw którym jego wartość może być reprezentowany. [C ++ §2.14.2 / 2] Dlatego teraz nie ma potrzeby dodawania sufiksu „LL” do literału liczby całkowitej, który jest zbyt duży dla innych typów.
bames53
8
Powodem, dla którego wcześniej był to problem, nie był fakt, że C ++ nie był wystarczająco `` inteligentny '', aby określić typ literału na podstawie typu przypisanej zmiennej, po prostu dlatego, że rozszerzenie kompilatora nie zaimplementowało rozszerzonej liczby całkowitej wpisz tak, aby dobrze działało z językiem standardowym. C ++ ma teraz takie zasady, że wszystkie rozszerzone typy liczb całkowitych będą lepiej zintegrowane ze standardem: open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1988.pdf
bames53
4
@unwind Myślę, że odpowiedź powinna zostać zredagowana zgodnie z tymi sugestiami.
Antonio
26

Próbować:

num3 = 100000000000LL;

A tak przy okazji, w C ++ jest to rozszerzenie kompilatora, standard nie definiuje long long, to część C99.

Arkaitz Jimenez
źródło
11
C ++ 11 teraz definiuje long long
Mohamed El-Nakib
4

To zależy w jakim trybie kompilujesz. long long nie jest częścią standardu C ++, ale jest obsługiwany tylko (zwykle) jako rozszerzenie. Wpływa to na rodzaj literałów. Dziesiętne literały liczb całkowitych bez sufiksu są zawsze typu int, jeśli liczba int jest wystarczająco duża aby reprezentować liczbę, w przeciwnym razie long. Jeśli liczba jest zbyt duża przez długi czas, wynik jest zdefiniowany przez implementację (prawdopodobnie tylko liczba typu long int, która została obcięta w celu zapewnienia zgodności z poprzednimi wersjami). W takim przypadku musisz jawnie użyć sufiksu LL, aby włączyć długie, długie rozszerzenie (w większości kompilatorów).

Następna wersja C ++ będzie oficjalnie obsługiwać long long w taki sposób, że nie będziesz potrzebować żadnego sufiksu, chyba że jawnie chcesz, aby typ literału był co najmniej długi. Jeśli liczba nie może być reprezentowana w postaci długiej, kompilator automatycznie spróbuje użyć long long, nawet bez sufiksu LL. Uważam, że jest to również zachowanie C99.

sellibitze
źródło
1

twój kod kompiluje się tutaj dobrze (nawet jeśli ta linia nie jest komentowana. Musiałem zmienić to na

num3 = 100000000000000000000;

aby zacząć otrzymywać ostrzeżenie.

Omry Yadan
źródło
Jaki kompilator? W C ++, literał liczby całkowitej jest mniejszą z wartości int lub long, do której pasuje. W C99 jest to najmniejsza z wartości int, long, long long. Tak więc, gdy długo próbujesz używać C ++ jako niestandardowego rozszerzenia, być może Twój kompilator przyjął również reguły C99 dla literałów.
Steve Jessop
gcc w wersji 4.3.2 (Debian 4.3.2-1.1) na 64-bitowym systemie Linux.
Omry Yadan
@SteveJessop Być może trochę za późno: ale długość niekoniecznie to 64 bity. W większości przypadków tak jest, ale nie masz gwarancji, że będzie wszędzie. Jedyną gwarancją, jaką masz, jest to, że jest co najmniej tak duże jak int, które z kolei jest co najmniej tak duże jak short int, które z kolei jest co najmniej tak duże jak char. Wreszcie znak char jest zdefiniowany jako wystarczająco duży, aby reprezentować każdy znak w podstawowym zestawie znaków implementacji (zwykle 8-bitowych).
pauluss86
@ pauluss86: Nie mówiłem o gwarancjach. Omry powiedział, że używa gcc 4.3.2 w 64-bitowym systemie Debian. Zauważyłem, że wyjaśniło to, co zobaczył, ponieważ (tak się złożyło, że wiem z ogólnej wiedzy) gcc jest domyślnie skonfigurowany w takich systemach do używania 64-bitowego, longzgodnie z ABI LP64 tego systemu operacyjnego.
Steve Jessop
@SteveJessop Nie sugeruję, że Twój komentarz jest zły! Jedynie wskazanie, że założenie, że long to zawsze 64 bity wszędzie, o czym niestety wiele osób myśli, jest niebezpieczne.
pauluss86