Cosa appartiene a uno strumento educativo per dimostrare i presupposti ingiustificati che le persone fanno in C / C ++?


121

Vorrei preparare un piccolo strumento educativo per SO che dovrebbe aiutare i programmatori principianti (e intermedi) a riconoscere e sfidare i loro presupposti ingiustificati in C, C ++ e le loro piattaforme.

Esempi:

  • "numeri interi a capo"
  • "ognuno ha ASCII"
  • "Posso memorizzare un puntatore a funzione in un vuoto *"

Ho pensato che un piccolo programma di test potrebbe essere eseguito su varie piattaforme, che esegue i presupposti "plausibili" che, dalla nostra esperienza in SO, di solito sono fatti da molti sviluppatori mainstream inesperti / semiesperti e registrano i modi in cui si rompono su macchine diverse.

L'obiettivo di questo non è quello di dimostrare che è "sicuro" fare qualcosa (cosa impossibile, i test provano solo qualcosa se si rompono), ma invece di dimostrare anche all'individuo più incapace di comprendere come l'espressione più poco appariscente interruzione su una macchina diversa, se ha un comportamento non definito o definito dall'implementazione. .

Per raggiungere questo obiettivo vorrei chiederti:

  • Come si può migliorare questa idea?
  • Quali test sarebbero buoni e come dovrebbero apparire?
  • Eseguiresti i test sulle piattaforme su cui puoi mettere le mani e pubblicheresti i risultati, in modo da ottenere un database di piattaforme, come differiscono e perché questa differenza è consentita?

Ecco la versione attuale per il giocattolo di prova:

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
int count=0;
int total=0;
void expect(const char *info, const char *expr)
{
    printf("..%s\n   but '%s' is false.\n",info,expr);
    fflush(stdout);
    count++;
}
#define EXPECT(INFO,EXPR) if (total++,!(EXPR)) expect(INFO,#EXPR)

/* stack check..How can I do this better? */
ptrdiff_t check_grow(int k, int *p)
{
    if (p==0) p=&k;
    if (k==0) return &k-p;
    else return check_grow(k-1,p);
}
#define BITS_PER_INT (sizeof(int)*CHAR_BIT)

int bits_per_int=BITS_PER_INT;
int int_max=INT_MAX;
int int_min=INT_MIN;

/* for 21 - left to right */
int ltr_result=0;
unsigned ltr_fun(int k)
{
    ltr_result=ltr_result*10+k;
    return 1;
}

int main()
{
    printf("We like to think that:\n");
    /* characters */
    EXPECT("00 we have ASCII",('A'==65));
    EXPECT("01 A-Z is in a block",('Z'-'A')+1==26);
    EXPECT("02 big letters come before small letters",('A'<'a'));
    EXPECT("03 a char is 8 bits",CHAR_BIT==8);
    EXPECT("04 a char is signed",CHAR_MIN==SCHAR_MIN);

    /* integers */
    EXPECT("05 int has the size of pointers",sizeof(int)==sizeof(void*));
    /* not true for Windows-64 */
    EXPECT("05a long has at least the size of pointers",sizeof(long)>=sizeof(void*));

    EXPECT("06 integers are 2-complement and wrap around",(int_max+1)==(int_min));
    EXPECT("07 integers are 2-complement and *always* wrap around",(INT_MAX+1)==(INT_MIN));
    EXPECT("08 overshifting is okay",(1<<bits_per_int)==0);
    EXPECT("09 overshifting is *always* okay",(1<<BITS_PER_INT)==0);
    {
        int t;
        EXPECT("09a minus shifts backwards",(t=-1,(15<<t)==7));
    }
    /* pointers */
    /* Suggested by jalf */
    EXPECT("10 void* can store function pointers",sizeof(void*)>=sizeof(void(*)()));
    /* execution */
    EXPECT("11 Detecting how the stack grows is easy",check_grow(5,0)!=0);
    EXPECT("12 the stack grows downwards",check_grow(5,0)<0);

    {
        int t;
        /* suggested by jk */
        EXPECT("13 The smallest bits always come first",(t=0x1234,0x34==*(char*)&t));
    }
    {
        /* Suggested by S.Lott */
        int a[2]={0,0};
        int i=0;
        EXPECT("14 i++ is strictly left to right",(i=0,a[i++]=i,a[0]==1));
    }
    {
        struct {
            char c;
            int i;
        } char_int;
        EXPECT("15 structs are packed",sizeof(char_int)==(sizeof(char)+sizeof(int)));
    }
    {
        EXPECT("16 malloc()=NULL means out of memory",(malloc(0)!=NULL));
    }

    /* suggested by David Thornley */
    EXPECT("17 size_t is unsigned int",sizeof(size_t)==sizeof(unsigned int));
    /* this is true for C99, but not for C90. */
    EXPECT("18 a%b has the same sign as a",((-10%3)==-1) && ((10%-3)==1));

    /* suggested by nos */
    EXPECT("19-1 char<short",sizeof(char)<sizeof(short));
    EXPECT("19-2 short<int",sizeof(short)<sizeof(int));
    EXPECT("19-3 int<long",sizeof(int)<sizeof(long));
    EXPECT("20 ptrdiff_t and size_t have the same size",(sizeof(ptrdiff_t)==sizeof(size_t)));
#if 0
    {
        /* suggested by R. */
        /* this crashed on TC 3.0++, compact. */
        char buf[10];
        EXPECT("21 You can use snprintf to append a string",
               (snprintf(buf,10,"OK"),snprintf(buf,10,"%s!!",buf),strcmp(buf,"OK!!")==0));
    }
#endif

    EXPECT("21 Evaluation is left to right",
           (ltr_fun(1)*ltr_fun(2)*ltr_fun(3)*ltr_fun(4),ltr_result==1234));

    {
    #ifdef __STDC_IEC_559__
    int STDC_IEC_559_is_defined=1;
    #else 
    /* This either means, there is no FP support
     *or* the compiler is not C99 enough to define  __STDC_IEC_559__
     *or* the FP support is not IEEE compliant. */
    int STDC_IEC_559_is_defined=0;
    #endif
    EXPECT("22 floating point is always IEEE",STDC_IEC_559_is_defined);
    }

    printf("From what I can say with my puny test cases, you are %d%% mainstream\n",100-(100*count)/total);
    return 0;
}

Oh, e ho creato questo wiki della comunità fin dall'inizio perché ho pensato che le persone volessero modificare il mio blabber quando leggono questo.

AGGIORNAMENTO Grazie per il tuo contributo. Ho aggiunto alcuni casi dalle tue risposte e vedrò se posso impostare un GitHub per questo come suggerito da Greg.

AGGIORNAMENTO : ho creato un repository GitHub per questo, il file è "gotcha.c":

Si prega di rispondere qui con patch o nuove idee, in modo che possano essere discusse o chiarite qui. Li unirò a gotcha.c allora.


7
Considera il modello medio in DOS. Le funzioni possono essere memorizzate in più segmenti, quindi un puntatore a funzione è lungo 32 bit. Ma i tuoi dati sono memorizzati solo in un singolo segmento, quindi i puntatori ai dati sono lunghi solo 16 bit. Poiché void * è un puntatore ai dati, è largo 16 bit, quindi non puoi inserire un puntatore a funzione in uno. Vedere c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David dato l'

6
Forse potresti lanciare questo codice su github.com o qualcosa del genere e poi le persone potrebbero facilmente contribuire con le patch.
Greg Hewgill

1
Molte cose qui dovrebbero aiutare: stackoverflow.com/questions/367633/…
Martin York,

4
POSIX richiede che i puntatori a funzione abbiano la stessa rappresentazione di void * e possano essere convertiti (con un cast) senza perdita di informazioni. Uno dei motivi è che dlsym()restituisce un void * ma è inteso sia per i puntatori di dati che per quelli a funzione. Pertanto potrebbe non essere così male dipendere da questo.
jilles

3
@tristopia: il punto 15 è qui, perché molti principianti sono spesso sorpresi di apprendere che i dati non sono impacchettati continuamente ma invece allineati a determinati confini. Sono perplessi quando cambiano l'ordine dei membri e ottengono dimensioni di oggetti diverse. Inoltre, l'imballaggio è la modalità predefinita con molti microcontrollori contemporanei o dispositivi incorporati. Anche il mio output AVR Atmega e TurboC / MSDOS è pieno. MSDOS è ancora utilizzato nelle applicazioni industriali.
Nordic Mainframe

Risposte:


91

L'ordine di valutazione delle sottoespressioni, incluso

  • gli argomenti di una chiamata di funzione e
  • operandi di operatori (ad esempio, +, -, =, *, /), ad eccezione di:
    • gli operatori logici binari ( &&e ||),
    • l'operatore condizionale ternario ( ?:) e
    • l'operatore virgola ( ,)

non è specificato

Per esempio

  int Hello()
  {
       return printf("Hello"); /* printf() returns the number of 
                                  characters successfully printed by it
                               */
  }

  int World()
  {
       return printf("World !");
  }

  int main()
  {

      int a = Hello() + World(); //might print Hello World! or World! Hello
      /**             ^
                      | 
                Functions can be called in either order
      **/
      return 0;
  } 

1
L'ho sempre saputo sui parametri delle funzioni, ma non ci ho mai pensato in termini di operatori ... ... e se mai ti vedessi scrivere codice del genere in un ambiente di produzione, ti darò uno schiaffo con una pasta bagnata.
riwalk

3
@Billy: ma solo per le versioni primitive degli operatori.
Dennis Zickefoose

1
@ Dennis: è vero. (Ecco perché è un elemento in C ++ efficace / più efficace per non sovraccaricarli mai (a meno che tu non stia scrivendo boost::spirit)
Billy ONeal

1
@ Daniel: non sono sicuro di cosa stai cercando di dire. Sembra che tu stia suggerendo che va bene sovraccaricare gli operatori perché sono solo gli utenti della tua classe che potrebbero sbagliare, e se non stai scrivendo in C ++ non importa. Nessuno dei due ha alcun senso.
Dennis Zickefoose

2
@ user420536: il comportamento è semplicemente non specificato ma non indefinito. Sì, l'esempio può stampare sia Hello World! o mondo! Ciao, ma non è specificato perché l'ordine di valutazione degli operandi +dell'operatore non è specificato (gli autori del compilatore non devono documentare il comportamento). Non viola alcuna regola dei punti sequenza in quanto tale.
Prasoon Saurav

38

sdcc 29.7 / ucSim / Z80

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..19-2 short<int
   but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
   but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
From what I can say with my puny test cases, you are Stop at 0x0013f3: (106) Invalid instruction 0x00dd

printf si arresta in modo anomalo. "O_O"


gcc 4.4@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 79% mainstream

gcc 4.4@x86_64-suse-linux (-O2)

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 82% mainstream

clang 2.7@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 72% mainstream

open64 4.2.3@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

intel 11.1@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

Turbo C ++ / DOS / Memoria ridotta

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 81% mainstream

Turbo C ++ / DOS / Memoria media

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..10 void* can store function pointers
but 'sizeof(void*)>=sizeof(void(*)())' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 78% mainstream

Turbo C ++ / DOS / Memoria compatta

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..20 ptrdiff_t and size_t have the same size
but '(sizeof(ptrdiff_t)==sizeof(size_t))' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 75% mainstream

cl65 @ Commodore PET (vice emulatore)

testo alternativo


Li aggiornerò più tardi:


Borland C ++ Builder 6.0 su Windows XP

..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

Visual Studio Express 2010 C ++ CLR, Windows 7 64 bit

(deve essere compilato come C ++ perché il compilatore CLR non supporta il C puro)

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

MINGW64 (gcc-4.5.2 prerelase)

- http://mingw-w64.sourceforge.net/

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..05a long has at least the size of pointers
   but 'sizeof(long)>=sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 67% mainstream

Windows a 64 bit utilizza il modello LLP64: Entrambi inte longsono definiti a 32 bit, il che significa che nessuno dei due è abbastanza lungo per un puntatore.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Le ipotesi fallite sono:

..14 i++ is structly left to right
..16 malloc()=NULL means out of memory
..19-2 short<int
..21 Evaluation is left to right
..22 floating point is always IEEE

L'Atmega168 ha un PC a 16 bit, ma codice e dati si trovano in spazi di indirizzi separati. Atmegas più grandi hanno un PC a 22 bit !.


gcc 4.2.1 su MacOSX 10.6, compilato con -arch ppc

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits come always first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream


32
E hai identificato un altro presupposto: che puoi inserire 80 caratteri su una riga terminale.
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())sarebbe più rilevante di ==. Tutto quello che ci interessa è "possiamo memorizzare un puntatore a funzione in un puntatore vuoto", quindi il presupposto che devi verificare è se a void*è grande almeno quanto un puntatore a funzione.
jalf

1
Se il tuo ambiente è conforme a POSIX, dovresti essere d'accordo con sizeof(void*)>=sizeof(void(*)())- vedi opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker

26

Molto tempo fa, stavo insegnando C da un libro di testo che aveva

printf("sizeof(int)=%d\n", sizeof(int));

come domanda di esempio. Non è riuscito per uno studente, perché sizeofrestituisce valori di tipo size_t, no int, intsu questa implementazione era di 16 bit e size_t32, ed era big-endian. (La piattaforma era Lightspeed C su Macintosh basati su 680x0. Ho detto che è stato molto tempo fa.)


7
+1 per aver segnalato uno degli errori di questo tipo più comuni e comunemente trascurati.
R .. GitHub STOP AIUTO AL GHIACCIO

4
Ciò accade anche su sistemi a 64 bit, dove size_t è 64 bit e gli int sono quasi sempre più brevi. Win64 è ancora più strano, perché size_t è unsigned long longlì. Aggiunto come Test 17.
Nordic Mainframe

Sfortunatamente, il runtime C di Microsoft non supporta il zmodificatore per size_tinteri dimensionati e long longnon è supportato anche su alcune piattaforme. Quindi non esiste un modo portatile sicuro per formattare o eseguire il cast della dimensione stampata di un oggetto.
Phil Miller

15

Devi includere le ipotesi ++e le --persone che fanno.

a[i++]= i;

Ad esempio, è sintatticamente legale, ma produce risultati variabili a seconda di troppe cose su cui ragionare.

Qualsiasi affermazione che ha ++(o --) e una variabile che si verifica più di una volta è un problema.


Ed è anche una domanda così comune!
Matthieu M.

8

Molto interessante!

Altre cose a cui posso pensare potrebbe essere utile controllare:

  • esistono puntatori a funzione e puntatori dati nello stesso spazio degli indirizzi? (Si interrompe l'architettura di macchine di Harvard come la modalità piccola DOS. Non so come testarlo, però.)

  • se si prende un puntatore a dati NULL e lo si cast sul tipo intero appropriato, ha il valore numerico 0? (Si interrompe su alcune macchine molto antiche --- vedere http://c-faq.com/null/machexamp.html .) Idem con il puntatore a funzione. Inoltre, potrebbero essere valori diversi.

  • l'incremento di un puntatore oltre la fine del suo oggetto di memorizzazione corrispondente, e poi di nuovo indietro, causa risultati sensati? (Non conosco nessuna macchina su cui si interrompe effettivamente, ma credo che la specifica C non ti consenta nemmeno di pensare a puntatori che non puntano a (a) il contenuto di un array o (b) l'elemento immediatamente dopo l'array o (c) NULL. Vedere http://c-faq.com/aryptr/non0based.html .)

  • Il confronto di due puntatori a diversi oggetti di archiviazione con <e> produce risultati coerenti? (Posso immaginare questa interruzione su macchine esotiche basate su segmenti; la specifica proibisce tali confronti, quindi il compilatore avrebbe il diritto di confrontare solo la parte offset del puntatore e non la parte del segmento.)

Hmm. Proverò a pensarne ancora.

Modifica: aggiunti alcuni collegamenti chiarificatori alle eccellenti FAQ C.


2
Per inciso, qualche tempo fa ho realizzato un progetto sperimentale chiamato Clue ( cluecc.sourceforge.net ) che ti ha permesso di compilare C in Lua, Javascript, Perl, LISP, ecc. Ha sfruttato spietatamente il comportamento indefinito nello standard C per far funzionare i puntatori . Potrebbe essere interessante provare questo test su di esso.
David dato l'

1
IIRC C consente di incrementare un puntatore di 1 oltre la fine di un oggetto, ma non oltre. Tuttavia, non è consentito decrementarlo in una posizione prima dell'inizio di un oggetto.
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

@R. Lo stesso in C ++. E un ulteriore incremento potrebbe interrompersi se l'incremento del puntatore causa un overflow, su CPU che non trattano i puntatori solo come numeri interi.
jalf

5

Penso che dovresti sforzarti di distinguere tra due classi molto diverse di ipotesi "errate". Una buona metà (spostamento a destra e estensione del segno, codifica compatibile con ASCII, la memoria è lineare, i puntatori di dati e funzioni sono compatibili, ecc.) Sono presupposti abbastanza ragionevoli da fare per la maggior parte dei codificatori C e potrebbero anche essere inclusi come parte dello standard se C venisse progettato oggi e se non avessimo legacy spazzatura IBM acquisita. L'altra metà (cose relative all'aliasing della memoria, comportamento delle funzioni di libreria quando la memoria di input e output si sovrappongono, ipotesi a 32 bit come che i puntatori si adattino into che puoi usaremalloc senza un prototipo, quella convenzione di chiamata è identica per le funzioni variadiche e non variadiche, ...) o è in conflitto con le ottimizzazioni che i compilatori moderni vogliono eseguire o con la migrazione su macchine a 64 bit o altre nuove tecnologie.


non è solo "spazzatura IBM" (anche se sono d'accordo che la roba IBM è spazzatura). Molti sistemi embedded oggi hanno problemi simili.
rmeador

Per chiarire, utilizzare mallocsenza un prototipo significa non includere <stdlib.h>, che fa sì che mallocper impostazione predefinita int malloc(int), un no-no se si desidera supportare 64 bit.
Joey Adams

Tecnicamente sei libero di non includere <stdlib.h>fintanto che includi un'altra intestazione che definisce size_te quindi dichiari tu stesso malloccon un prototipo corretto.
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

5

Eccone uno divertente: cosa c'è di sbagliato in questa funzione?

float sum(unsigned int n, ...)
{
    float v = 0;
    va_list ap;
    va_start(ap, n);
    while (n--)
        v += va_arg(ap, float);
    va_end(ap);
    return v;
}

[Risposta (rot13): Inevnqvp nethzragf borl gur byq X&E cebzbgvba ehyrf, juvpu zrnaf lbh pnaabg hfr 'sybng' (be 'pune' be 'fubeg') va in_net! Naq gur pbzcvyre vf erdhverq abg gb gerng guvf nf n pbzcvyr-gvzr reebe. (TPP qbrf rzvg n jneavat, gubhtu.)]


Oh, quello è buono. clang 2.7 mangia questo e produce assurdità complete senza preavviso.
Nordic Mainframe

va_arg si espande se è una macro e il ciclo while esegue solo la prima istruzione, forse molte?
Maister

No (se ciò accadesse sarebbe un bug nell'implementazione).
Zwol

5
EXPECT("## pow() gives exact results for integer arguments", pow(2, 4) == 16);

Un altro riguarda la modalità testo in fopen. La maggior parte dei programmatori presume che testo e binario siano gli stessi (Unix) o che la modalità testo aggiunga \rcaratteri (Windows). Ma C è stato portato su sistemi che utilizzano record a larghezza fissa, su cui fputc('\n', file)in un file di testo si intende aggiungere spazi o qualcosa del genere fino a quando la dimensione del file è un multiplo della lunghezza del record.

Ed ecco i miei risultati:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 su x86-64

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 78% mainstream

In realtà ho visto il codice combinato pow(2, n)con le operazioni sui bit.
dan04

4

Alcuni di essi non possono essere facilmente testati dall'interno di C perché è probabile che il programma vada in crash sulle implementazioni in cui l'assunto non è valido.


"Va bene fare qualsiasi cosa con una variabile con valore di puntatore. Deve solo contenere un valore di puntatore valido se la si dereferenzia."

void noop(void *p); /* A no-op function that the compiler doesn't know to optimize away */
int main () {
    char *p = malloc(1);
    free(p);
    noop(p); /* may crash in implementations that verify pointer accesses */
    noop(p - 42000); /* and if not the previous instruction, maybe this one */
}

Lo stesso unsigned charvale per i tipi a virgola mobile e integrale (diversi da ), che possono avere rappresentazioni trap.


"I calcoli interi vanno a capo. Quindi questo programma stampa un grande numero intero negativo."

#include <stdio.h>
int main () {
    printf("%d\n", INT_MAX+1); /* may crash due to signed integer overflow */
    return 0;
}

(Solo C89.) "Va bene cadere alla fine di main."

#include <stdio.h>
int main () {
    puts("Hello.");
} /* The status code is 7 on many implementations. */

2
Come esempio concreto: quando compilato con gcc -ftrapv -O, l'output è We like to think that:seguito daAborted
caf

@caf: "Questa opzione genera trap per overflow con segno su operazioni di addizione, sottrazione e moltiplicazione." Bello sapere, grazie.
Gilles "SO- smettila di essere malvagio"

1
L'ultimo è ok anche in C ++ (98, 03 e 0x) e restituisce implicitamente 0.
jalf

Il che è brutto perché pre-ANSI C lo consentiva e anche C99 lo fa.
Joshua

@ Joshua: AFAIK non c'è differenza tra pre-ANSI C e C89 al ritorno da mainsenza valore: il programma è corretto ma restituisce uno stato di terminazione indefinito (C89 §2.1.2.2). Con molte implementazioni (come gcc e vecchi compilatori unix) ottieni tutto ciò che era in un certo registro a quel punto. Il programma in genere funziona fino a quando non viene utilizzato in un makefile o in un altro ambiente che controlla lo stato di terminazione.
Gilles 'SO- smettila di essere malvagio'

4

Ebbene, le classiche ipotesi di portabilità non ancora espresse lo sono

  • ipotesi sulla dimensione dei tipi integrali
  • endianness

4
"Endianness", incluso "There is an endianness": ci sono macchine middle-endian, e lo standard consente cose strane come memorizzare un shortvalore fedcab9876543210 (che è 16 cifre binarie) come due byte 0248ace e fdb97531.
Gilles 'SO- smettila di essere malvagio'

sì, l'endianess include sicuramente l'endian misto / medio così come il grande e il piccolo. se vai su hardware personalizzato potresti avere qualsiasi endianess che ti piace su qualsiasi bus.
jk.

L'endian medio è noto come endian PDP. Gilles descrive qualcosa di ancora più strano che potrebbe causare mal di testa per l'implementazione di TCP / IP.
Joshua

@Gilles: middle-endian ... sono molto contento di non svilupparmi su quello. (ma ora mi verrà chiesto di fare un progetto di rete di fascia media, ne sono sicuro) ...
Paul Nathan

ARM FPE utilizzava i doppi middle-endian, dove venivano memorizzati come una coppia <high ​​quad> <low quad> ma l'ordine dei bit all'interno di ogni quad era sbagliato. (Per fortuna, ARM VFP non lo fa più.)
David Given

4
  • Errori di discrezionalità dovuti alla rappresentazione in virgola mobile. Ad esempio, se si utilizza la formula standard per risolvere equazioni quadratiche, o differenze finite per approssimare le derivate, o la formula standard per calcolare le varianze, la precisione andrà persa a causa del calcolo delle differenze tra numeri simili. L'algoritmo di Gauß per risolvere i sistemi lineari è sbagliato perché gli errori di arrotondamento si accumulano, quindi si usa la decomposizione QR o LU, la decomposizione di Cholesky, SVD, ecc. L'aggiunta di numeri in virgola mobile non è associativa. Sono disponibili valori denormali, infiniti e NaN. a + b - ab .

  • Stringhe: differenza tra caratteri, punti di codice e unità di codice. Come viene implementato Unicode sui vari sistemi operativi; Codifiche Unicode. L'apertura di un file con un nome di file Unicode arbitrario non è possibile con C ++ in modo portatile.

  • Race condition, anche senza threading: testando l'esistenza di un file il risultato potrebbe non essere più valido in qualsiasi momento.

  • ERROR_SUCCESS = 0


4

Includere un controllo per le dimensioni intere. La maggior parte delle persone presume che un int sia più grande di uno short è più grande di un char. Tuttavia, questi potrebbero essere tutti falsi:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Questo codice potrebbe non riuscire (si arresta in modo anomalo per l'accesso non allineato)

unsigned char buf[64];

int i = 234;
int *p = &buf[1];
*p = i;
i = *p;

questo codice fallirebbe in C ++? IIRC, è illegale lanciare puntatori tra tipi non correlati, TRANNE per char *, che può essere lanciato a qualsiasi tipo (o è il contrario?).
rmeador

1
Potresti farlo solo int *p = (int*)&buf[1];in c ++, le persone si aspettano che funzioni anche.
n.

@nos, sì, può fallire ma il fallimento è un crash, quindi il suo programma non può testarlo. :(
Joshua

1
sizeof(char) < sizeof(int)è obbligatorio. Ad esempio, fgetc () restituisce il valore del carattere come un carattere senza segno convertito in int o EOFche è un valore negativo. unsigned charpotrebbe non avere bit di riempimento, quindi l'unico modo per farlo è rendere int maggiore di char. Inoltre, (la maggior parte delle versioni) la specifica C richiede che qualsiasi valore compreso nell'intervallo -32767..32767 possa essere memorizzato in un int.
jilles

@illes ancora, ci sono DSP con caratteri a 32 bit e int a 32 bit.
nn.

3

Un paio di cose sui tipi di dati incorporati:

  • chare signed charsono in realtà due tipi distinti (a differenza inte signed intche si riferiscono allo stesso tipo di intero con segno).
  • gli interi con segno non sono necessari per utilizzare il complemento a due. Anche il complemento e il segno + grandezza di uno sono rappresentazioni valide di numeri negativi. Ciò rende le operazioni sui bit che coinvolgono numeri negativi definite dall'implementazione .
  • Se si assegna un numero intero fuori intervallo a una variabile intera con segno, il comportamento è definito dall'implementazione .
  • In C90, -3/5potrebbe tornare 0o -1. L'arrotondamento verso lo zero nel caso in cui un operando fosse negativo è garantito solo in C99 verso l'alto e C ++ 0x verso l'alto.
  • Non ci sono garanzie di dimensioni esatte per i tipi incorporati. Lo standard copre solo i requisiti minimi come un intha almeno 16 bit, a longha almeno 32 bit, a long longha almeno 64 bit. A floatpuò rappresentare correttamente almeno 6 cifre decimali più significative. A doublepuò rappresentare correttamente almeno 10 cifre decimali più significative.
  • IEEE 754 non è obbligatorio per rappresentare i numeri in virgola mobile.

Certo, sulla maggior parte delle macchine avremo il complemento a due e i float IEEE 754.


Mi chiedo quale valore ci sia nell'avere assegnazioni di numeri interi fuori intervallo definite dall'implementazione piuttosto che un comportamento indefinito? Su alcune piattaforme, tale requisito costringerebbe il compilatore a generare codice extra per int mult(int a,int b) { return (long)a*b;}[ad esempio, se intè di 32 bit, ma registra e longsono 64]. Senza tale requisito, il comportamento "naturale" dell'implementazione più rapida di long l=mult(1000000,1000000);sarebbe impostato luguale a 1000000000000, anche se questo è un valore "impossibile" per un int.
supercat

3

Che ne dici di questo:

Nessun puntatore ai dati può mai essere uguale a un puntatore a funzione valido.

Questo è VERO per tutti i modelli piatti, i modelli MS-DOS TINY, LARGE e HUGE, falso per il modello MS-DOS SMALL e quasi sempre falso per i modelli MEDIUM e COMPACT (dipende dall'indirizzo di caricamento, avrai bisogno di un DOS molto vecchio per rendilo vero).

Non posso scrivere un test per questo

E peggio: i puntatori lanciati a ptrdiff_t possono essere confrontati. Questo non è vero per il modello MS-DOS LARGE (l'unica differenza tra LARGE e HUGE è che ENORME aggiunge il codice del compilatore per normalizzare i puntatori).

Non riesco a scrivere un test perché l'ambiente in cui questo bombe forte non allocherà un buffer maggiore di 64K, quindi il codice che lo dimostra si bloccherebbe su altre piattaforme.

Questo particolare test passerebbe su un sistema ormai defunto (nota che dipende dagli interni di malloc):

  char *ptr1 = malloc(16);
  char *ptr2 = malloc(16);
  if ((ptrdiff_t)ptr2 - 0x20000 == (ptrdiff_t)ptr1)
      printf("We like to think that unrelated pointers are equality comparable when cast to the appropriate integer, but they're not.");

3

EDIT: aggiornato all'ultima versione del programma

Solaris SPARC

gcc 3.4.6 a 32 bit

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 72% mainstream

gcc 3.4.6 a 64 bit

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 68% mainstream

e con SUNStudio 11 32 bit

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
From what I can say with my puny test cases, you are 79% mainstream

e con SUNStudio 11 64 bit

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 75% mainstream

2

Puoi usare la modalità testo ( fopen("filename", "r")) per leggere qualsiasi tipo di file di testo.

Anche se in teoria dovrebbe funzionare bene, se lo usi anche ftell()nel tuo codice e il tuo file di testo ha terminazioni di riga in stile UNIX, in alcune versioni della libreria standard di Windows, ftell()spesso restituiranno valori non validi. La soluzione è quella di utilizzare la modalità binaria invece ( fopen("filename", "rb")).


1

gcc 3.3.2 su AIX 5.3 (sì, dobbiamo aggiornare gcc)

We like to think that:
..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits come always first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

1

Un presupposto che alcuni possono fare in C ++ è che a structè limitato a ciò che può fare in C. Il fatto è che, in C ++, a structè come a classtranne che ha tutto pubblico per impostazione predefinita.

C ++ struct:

struct Foo
{
  int number1_;  //this is public by default


//this is valid in C++:    
private: 
  void Testing1();
  int number2_;

protected:
  void Testing2();
};

1

Le funzioni matematiche standard su sistemi diversi non danno risultati identici.


1

Visual Studio Express 2010 su x86 a 32 bit.

Z:\sandbox>cl testtoy.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

testtoy.c
testtoy.c(54) : warning C4293: '<<' : shift count negative or too big, undefined
 behavior
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:testtoy.exe
testtoy.obj

Z:\sandbox>testtoy.exe
We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

1

Tramite Codepad.org ( C++: g++ 4.1.2 flags: -O -std=c++98 -pedantic-errors -Wfatal-errors -Werror -Wall -Wextra -Wno-missing-field-initializers -Wwrite-strings -Wno-deprecated -Wno-unused -Wno-non-virtual-dtor -Wno-variadic-macros -fmessage-length=0 -ftemplate-depth-128 -fno-merge-constants -fno-nonansi-builtins -fno-gnu-keywords -fno-elide-constructors -fstrict-aliasing -fstack-protector-all -Winvalid-pch).

Notare che Codepad non aveva stddef.h. Ho rimosso il test 9 a causa del blocco dei codici che utilizzava gli avvisi come errori. Ho anche rinominato la countvariabile poiché era già definita per qualche motivo.

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
From what I can say with my puny test cases, you are 84% mainstream

1

Che ne dici di cambiare a destra di quantità eccessive - è consentito dallo standard o vale la pena testarlo?

Lo Standard C specifica il comportamento del seguente programma:

void print_string (char * st)
{
  char ch;
  mentre ((ch = * st ++)! = 0)
    putch (ch); / * Supponiamo che sia definito * /
}
int main (void)
{
  print_string ( "Ciao");
  return 0;
}

Su almeno un compilatore che uso, quel codice fallirà a meno che l'argomento di print_string non sia un "char const *". Lo standard consente tale restrizione?

Alcuni sistemi consentono di produrre puntatori a "int" non allineati e altri no. Potrebbe valere la pena provare.


C89 §3.3.7: "Se il valore dell'operando destro è negativo o è maggiore o uguale alla larghezza in bit dell'operando sinistro promosso, il comportamento non è definito." (si applica a entrambi <<e >>). C99 ha la stessa lingua in §6.5.7-3.
Gilles 'SO- smettila di essere malvagio'

A parte putch(perché non hai usato lo standard putchar?), Non riesco a vedere alcun comportamento indefinito nel tuo programma. C89 §3.1.4 specifica che "una stringa di caratteri letterale ha [...] tipo 'matrice di caratteri'" (nota: no const), e che "se il programma tenta di modificare una stringa letterale [...], il comportamento è indefinito" . Di che compilatore si tratta e come traduce questo programma?
Gilles "SO- smettila di essere cattivo"

2
In C ++ le costanti dei caratteri non sono char [], sono const char []. Tuttavia ... ci utilizzato per essere un buco specifico nel sistema di tipo per consentire di utilizzare una costante stringa in un contesto in cui ci si aspettava un char * e non ottiene un errore di tipo. Ciò ha portato a situazioni in cui print_string ("foo") avrebbe funzionato ma print_string ("foo" +0) no. Ciò creava confusione, in particolare negli ambienti in cui i file C vengono compilati utilizzando un compilatore C ++ per impostazione predefinita. Il buco è stato rimosso nei nuovi compilatori ma ce ne sono ancora molti vecchi in giro. AFAIK C99 definisce ancora le costanti di stringa come char [].
David dato il

1
Sui compilatori HiTech per la serie di controller Microchip PIC, un puntatore senza un qualificatore di archiviazione può puntare solo alla RAM. Un puntatore qualificato const può puntare a RAM o ROM. I puntatori non qualificati con const vengono dereferenziati direttamente nel codice; i puntatori qualificati con const vengono dereferenziati tramite la routine di libreria. A seconda del particolare tipo di PIC, i puntatori non qualificati con cost sono 1 o 2 byte; quelli qualificati per const sono 2 o 3. Poiché la ROM è molto più abbondante della RAM, avere costanti nella ROM è generalmente una buona cosa.
supercat

@David Given: nota anche il mio commento precedente. Preferisco i compilatori che utilizzano qualificatori diversi da "const" per denotare la classe di memoria hardware; il compilatore HiTech ha alcune stranezze piuttosto fastidiose con la sua allocazione della classe di archiviazione (ad esempio gli elementi di dati la cui "dimensione del componente" è un byte, o gli elementi di dati che superano i 256 byte, vanno in un segmento "grande". Altri elementi di dati vanno nel " bss "per il modulo in cui sono definiti; tutti gli elementi" bss "in un modulo devono rientrare in 256 byte. Gli array leggermente inferiori a 256 byte possono essere un vero fastidio.
supercat

0

Cordiali saluti, per coloro che devono tradurre le proprie competenze in C in Java, ecco alcuni trucchi.

EXPECT("03 a char is 8 bits",CHAR_BIT==8);
EXPECT("04 a char is signed",CHAR_MIN==SCHAR_MIN);

In Java, char è a 16 bit e firmato. byte è a 8 bit e con segno.

/* not true for Windows-64 */
EXPECT("05a long has at least the size of pointers",sizeof(long)>=sizeof(void*));

long è sempre a 64 bit, i riferimenti possono essere a 32 o 64 bit (se hai più di un'app con più di 32 GB) Le JVM a 64 bit in genere utilizzano riferimenti a 32 bit.

EXPECT("08 overshifting is okay",(1<<bits_per_int)==0);
EXPECT("09 overshifting is *always* okay",(1<<BITS_PER_INT)==0);

Lo spostamento è mascherato in modo che i << 64 == i == i << -64, i << 63 == i << -1

EXPECT("13 The smallest bits always come first",(t=0x1234,0x34==*(char*)&t));

ByteOrder.nativeOrder () può essere BIG_ENDIAN o LITTLE_ENDIAN

EXPECT("14 i++ is strictly left to right",(i=0,a[i++]=i,a[0]==1));

i = i++ Non cambia mai i

/* suggested by David Thornley */
EXPECT("17 size_t is unsigned int",sizeof(size_t)==sizeof(unsigned int));

La dimensione delle raccolte e degli array è sempre a 32 bit indipendentemente dal fatto che la JVM sia a 32 o 64 bit.

EXPECT("19-1 char<short",sizeof(char)<sizeof(short));
EXPECT("19-2 short<int",sizeof(short)<sizeof(int));
EXPECT("19-3 int<long",sizeof(int)<sizeof(long));

char è a 16 bit, short è a 16 bit, int è a 32 bit e long è a 64 bit.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.