Perché GCC emette un avviso quando si imposta un long senza segno a 2 ^ 64-1?

Lo standard C afferma che un long int è di almeno 4 byte – sul mio sistema sono 8 byte.

Ciò significa che posso memorizzare valori fino a 2 ^ 63 -1 in un long e 2 64 -1 in un unsigned long .

Tuttavia, quando il seguente codice viene compilato con il flag -Wall , viene visualizzato l’avviso [Wimplicitly-unsigned-literal] :

 int main (int argc, char ** argv) { unsigned long a; a = 18446744073709551615; // 2^64-1 } 

Se uso 2 63 -1 (9223372036854775807), invece, compila senza avvisi (come previsto – 2 63 -1 si inserirà in un signed long int ).

Per un progetto dovevo avere il valore massimo in un unsigned long e ho scoperto che (9223372036854775807 << 1) + 1 non ha (9223372036854775807 << 1) + 1 questo avviso. Il mio insegnante ha quindi suggerito di utilizzare ULONG_MAX definito in limits.h e questo non ha dato alcun avvertimento.

Perché non posso farlo senza avvertire che è stato convertito implicitamente – quando l’ho dichiarato esplicitamente?

Per lo standard C, il tipo di una costante decimale senza suffisso è int , long int o long long int , in particolare il primo di quelli che è sufficiente per rappresentare il valore. Nell’implementazione C, nessuno di questi può rappresentare 18446744073709551615, perché è troppo grande.

Per adattarti, il compilatore sta facendo il suo tipo unsigned long . Tecnicamente, questo non è conforms allo standard C, quindi il compilatore ti avverte.

In questo caso, non viene causato alcun danno, poiché si assegna il valore a un unsigned long . Ma ci sono situazioni in cui l’uso del tipo sbagliato può causare problemi, quindi in generale è necessario aggiungere un suffisso a tali costanti per assicurarsi che corrispondano al modo in cui devono essere utilizzate. In questo caso, u è sufficiente; come con i tipi non codificati, il compilatore deciderà se utilizzare unsigned int , unsigned long int o unsigned long long int seconda della grandezza del numero e delle capacità dei tipi.

L’hai dichiarato esplicitamente, ma senza U, che lo renderebbe senza firma. Poiché non esiste una costante intera con segno con questo valore, la rende implicitamente non firmata, fornendo le informazioni che è meglio renderla esplicita.

Fatelo con a = 18446744073709551615U; .

Dai un’occhiata a questo tavolo stravagante dello standard (6.4.4.1p5) :

tabella di promozione costante intera dallo standard

che spiega come i letterali interi sono adattati ai tipi interi.

In sostanza, poiché il letterale è decimale e non ha un suffisso, cerca di adattarsi ai seguenti tipi:

 int long int long long int 

Dal momento che non si adatta a long long int , stai ricevendo l’avviso che stai ricevendo.

Se aggiungi un suffisso U ( u ) (o UL o UL o le varianti minuscole):

 unsigned long a = 18446744073709551615U; 

non avrai questo problema, perché passerai alla sequenza di promozione:

 unsigned int unsigned long int unsigned long long int 

(o i sottoinsiemi appropriati per UL e UL ) e il letterale si adatta a unsigned long int (se questo è effettivamente il primo tipo senza segno con non meno di 64 bit utilizzabili, come di solito è).

In alternativa, puoi distriggersre l’avviso passando a un valore letterale ottale o esadecimale:

 unsigned long a = 0xFFFFFFFFFFFFFFFF; unsigned long octal_a = 01777777777777777777777; 

come per questi la sequenza di promozione è:

 int unsigned int long int unsigned long int long long int unsigned long long int 

come per la tabella collegata, che di nuovo, consente loro di adattarsi al primo tipo senza segno a 64 bit (solitamente unsigned long int ).

Quello che non dovresti assolutamente fare è:

 (9223372036854775807 << 1) + 1 //don't do this! 

poiché invoca un comportamento indefinito interrompendo 6.5.7p4 indipendentemente dal fatto che un avviso sia generato o meno.