¿Qué pertenece a una herramienta educativa para demostrar las suposiciones injustificadas que las personas hacen en C / C ++?

121

Me gustaría preparar una pequeña herramienta educativa para SO que debería ayudar a los programadores principiantes (e intermedios) a reconocer y desafiar sus suposiciones injustificadas en C, C ++ y sus plataformas.

Ejemplos:

  • "enteros envueltos"
  • "todos tienen ASCII"
  • "Puedo almacenar un puntero de función en un vacío *"

Pensé que un pequeño programa de prueba podría ejecutarse en varias plataformas, que ejecuta las suposiciones "plausibles" que, por nuestra experiencia en SO, generalmente son hechas por muchos desarrolladores principales inexpertos / semiexperimentados y registran las formas en que se rompen en diversas máquinas.

El objetivo de esto no es demostrar que es "seguro" hacer algo (lo que sería imposible de hacer, las pruebas prueban solo cualquier cosa si se rompen), sino demostrar al individuo más incomprensible cómo la expresión más discreta interrumpir en una máquina diferente, si tiene un comportamiento indefinido o definido de implementación. .

Para lograr esto, me gustaría preguntarte:

  • ¿Cómo se puede mejorar esta idea?
  • ¿Qué pruebas serían buenas y cómo deberían verse?
  • ¿Ejecutaría las pruebas en las plataformas en las que puede tener acceso y publicará los resultados, para que terminemos con una base de datos de plataformas, en qué se diferencian y por qué se permite esta diferencia?

Aquí está la versión actual para el juguete de prueba:

#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;
}

Ah, e hice este wiki de la comunidad desde el principio porque pensé que la gente quiere editar mi charlatanería cuando lean esto.

ACTUALIZACIÓN Gracias por su aporte. He agregado algunos casos a partir de sus respuestas y veré si puedo configurar un github para esto como Greg sugirió.

ACTUALIZACIÓN : he creado un repositorio github para esto, el archivo es "gotcha.c":

Responda aquí con parches o nuevas ideas, para que puedan discutirse o aclararse aquí. Los fusionaré en gotcha.c entonces.

Mainframe nórdico
fuente
77
Considere el modelo medio en DOS. Las funciones se pueden almacenar en varios segmentos, por lo que un puntero de función tiene 32 bits de longitud. Pero sus datos se almacenan en un solo segmento, por lo tanto, los punteros de datos tienen solo 16 bits de longitud. Como void * es un puntero de datos, tiene 16 bits de ancho, por lo que no puede ajustar un puntero de función en uno. Consulte c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David dado el
66
Tal vez podría lanzar este código en github.com o algo así y la gente podría contribuir fácilmente con parches.
Greg Hewgill
1
Muchas cosas aquí deberían ayudar: stackoverflow.com/questions/367633/…
Martin York
44
POSIX requiere que los punteros de función tengan la misma representación que void * y se puedan convertir (con una conversión) sin pérdida de información. Una de las razones de esto es que dlsym()devuelve un vacío * pero está destinado a punteros de datos y funciones. Por lo tanto, puede no ser tan malo depender de esto.
jilles
3
@tristopia: el punto 15 está aquí, porque muchos principiantes a menudo se sorprenden al saber que los datos no se empaquetan continuamente, sino que se alinean con ciertos límites. Están perplejos cuando cambian el orden de los miembros y obtienen diferentes tamaños de objeto. Además, el embalaje es el modo predeterminado con muchos microcontroladores contemporáneos o dispositivos integrados. Mi salida AVR Atmega y TurboC / MSDOS también está empaquetada. MSDOS todavía se usa en aplicaciones industriales.
Mainframe nórdico

Respuestas:

91

El orden de evaluación de subexpresiones, incluyendo

  • los argumentos de una llamada a la función y
  • operandos de los operadores (por ejemplo, +, -, =, *, /), con la excepción de:
    • los operadores lógicos binarios ( &&y ||),
    • el operador condicional ternario ( ?:), y
    • el operador de coma ( ,)

no está especificado

Por ejemplo

  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;
  } 
Prasoon Saurav
fuente
1
Siempre había sabido eso sobre los parámetros de función, pero nunca pensé en términos de operadores ... ... y si alguna vez te veo escribiendo código así en un entorno de producción, te daré una palmada con un fideo húmedo.
Riwalk
3
@Billy: Pero solo para las versiones primitivas de los operadores.
Dennis Zickefoose
1
@ Dennis: Eso es cierto. (Es por eso que es un elemento en C ++ efectivo / más efectivo para nunca sobrecargarlos (a menos que esté escribiendo boost::spirit)
Billy ONeal
1
@Daniel: No estoy seguro de lo que estás tratando de decir. Parece que está sugiriendo que está bien sobrecargar a los operadores porque solo los usuarios de su clase pueden equivocarse, y si no está escribiendo en C ++ directamente, no importa. Ninguno de los cuales tiene ningún sentido en absoluto.
Dennis Zickefoose
2
@ user420536: el comportamiento no está especificado pero no está definido. Sí, el ejemplo puede imprimir Hello World! o mundo! Hola, pero eso no está especificado porque el orden de evaluación de los operandos del +operador no está especificado (los escritores del compilador no necesitan documentar el comportamiento). No viola ninguna regla de punto de secuencia como tal.
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 se bloquea. "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 pequeña

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 compacta

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 emulador)

texto alternativo


Estaré actualizando estos más tarde:


Borland C ++ Builder 6.0 en 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 bits

(debe compilarse como C ++ porque el compilador CLR no admite 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 (pre-análisis gcc-4.5.2)

- 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 de 64 bits utiliza el modelo LLP64: ambos inty longse definen como 32 bits, lo que significa que ninguno de ellos es lo suficientemente largo para un puntero.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Los supuestos fallidos son:

..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

El Atmega168 tiene una PC de 16 bits, pero el código y los datos están en espacios de direcciones separados. ¡Atmegas más grande tiene una PC de 22 bits !.


gcc 4.2.1 en MacOSX 10.6, compilado 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

100%
fuente
32
Y ha identificado otra suposición: que puede caber 80 caracteres en una línea de terminal.
Mike Seymour
3
sizeof(void*)>=sizeof(void(*)())sería más relevante que ==. Lo único que nos importa es "podemos almacenar un puntero de función en un puntero vacío", por lo que la suposición que debe probar es si a void*es al menos tan grande como un puntero de función.
jalf
1
Si su entorno es compatible con POSIX, debería estar de acuerdo con sizeof(void*)>=sizeof(void(*)())- vea opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker
26

Hace mucho tiempo, estaba enseñando C de un libro de texto que tenía

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

como una pregunta de muestra Falló para un estudiante, porque sizeofproduce valores de tipo size_t, no int, inten esta implementación fue de 16 bits ysize_t fue de 32, y fue big-endian. (La plataforma era Lightspeed C en Macintoshes basados ​​en 680x0. Dije que fue hace mucho tiempo).

David Thornley
fuente
77
+1 por señalar uno de los errores más comunes y comúnmente ignorados de este tipo.
R .. GitHub DEJA DE AYUDAR AL HIELO
44
Esto también ocurre en sistemas de 64 bits, donde size_t es de 64 bits y las entradas son casi siempre más cortas. Win64 sigue siendo más extraño, porque size_t está unsigned long longahí. Agregado como prueba 17.
Mainframe nórdico
Desafortunadamente, el tiempo de ejecución C de Microsoft no admite el zmodificador para size_tenteros de tamaño, y long longtampoco es compatible con algunas plataformas. Por lo tanto, no hay una forma segura y portátil de formatear o emitir el tamaño impreso de un objeto.
Phil Miller
15

Debe incluir los supuestos ++y las --suposiciones que hacen.

a[i++]= i;

Por ejemplo, es sintácticamente legal, pero produce resultados variables dependiendo de demasiadas cosas para razonar.

Cualquier declaración que tenga ++(o --) y una variable que ocurra más de una vez es un problema.

S.Lott
fuente
¡Y es una pregunta tan común también!
Matthieu M.
8

¡Muy interesante!

Otras cosas que se me ocurren podrían ser útiles para verificar:

  • ¿existen punteros de función y punteros de datos en el mismo espacio de direcciones? (Se rompe en máquinas de arquitectura de Harvard como el modo pequeño de DOS. Sin embargo, no sé cómo lo probaría).

  • si toma un puntero de datos NULL y lo convierte al tipo entero apropiado, ¿tiene el valor numérico 0? (Rompe algunas máquinas realmente antiguas --- ver http://c-faq.com/null/machexamp.html .) Lo mismo ocurre con el puntero de función. Además, pueden ser valores diferentes.

  • ¿Incrementar un puntero más allá del final de su objeto de almacenamiento correspondiente y luego volver de nuevo, produce resultados razonables? (No conozco ninguna máquina en la que realmente se rompa, pero creo que la especificación C no te permite ni siquiera pensar en punteros que no apuntan a (a) el contenido de una matriz o (b) el elemento inmediatamente después de la matriz o (c) NULL. Consulte http://c-faq.com/aryptr/non0based.html .)

  • ¿compara dos punteros a diferentes objetos de almacenamiento con <y> produce resultados consistentes? (Puedo imaginar esta ruptura en máquinas exóticas basadas en segmentos; la especificación prohíbe tales comparaciones, por lo que el compilador tendría derecho a comparar solo la parte de desplazamiento del puntero, y no la parte del segmento).

Hmm Trataré de pensar en algo más.

Editar: se agregaron algunos enlaces aclaratorios a las excelentes preguntas frecuentes de C.

David dado
fuente
2
Por cierto, hace un tiempo hice un proyecto experimental llamado Clue ( cluecc.sourceforge.net ) que le permitió compilar C en Lua, Javascript, Perl, LISP, etc. Explotó sin piedad el comportamiento indefinido en el estándar C para hacer que los punteros funcionen . Puede ser interesante probar esta prueba en él.
David dado el
1
IIRC C le permite incrementar un puntero en 1 más allá del final de un objeto, pero no más. Sin embargo, no está permitido disminuirlo a una posición antes del comienzo de un objeto.
R .. GitHub DEJA DE AYUDAR AL HIELO
@R. Lo mismo en C ++. Y aumentar aún más podría romperse si al incrementar el puntero se produce un desbordamiento en las CPU que no solo tratan los punteros como enteros.
jalf
5

Creo que debería hacer un esfuerzo para distinguir entre dos clases muy diferentes de supuestos "incorrectos". Una buena mitad (desplazamiento a la derecha y extensión de signo, codificación compatible con ASCII, memoria es lineal, punteros de datos y funciones son compatibles, etc.) son suposiciones bastante razonables para la mayoría de los codificadores C, e incluso podrían incluirse como parte del estándar si C se estuviera diseñando hoy y si no tuviéramos el legado de basura basura de IBM. La otra mitad (cosas relacionadas con el alias de memoria, el comportamiento de las funciones de la biblioteca cuando la memoria de entrada y salida se superponen, suposiciones de 32 bits como esos punteros encajanint o que puede usarmalloc sin un prototipo, esa convención de llamada es idéntica para las funciones variadas y no variables, ...) entra en conflicto con las optimizaciones que los compiladores modernos desean realizar o con la migración a máquinas de 64 bits u otra tecnología nueva.

R .. GitHub DEJA DE AYUDAR AL HIELO
fuente
no es solo "basura de IBM" (aunque estoy de acuerdo en que las cosas de IBM son basura). Muchos sistemas integrados de hoy tienen problemas similares.
rmeador
Para aclarar, usar mallocsin un prototipo significa no incluir <stdlib.h>, lo que hace mallocque el valor predeterminado sea int malloc(int)un no-no si desea admitir 64 bits.
Joey Adams
Técnicamente, es libre de no incluir <stdlib.h>siempre que incluya otro encabezado que defina size_ty luego declare malloccon un prototipo correcto.
R .. GitHub DEJA DE AYUDAR AL HIELO
5

Aquí hay una divertida: ¿Qué tiene de malo esta función?

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;
}

[Respuesta (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.)]

zwol
fuente
Oh, esa es buena. clang 2.7 come esto y produce un sinsentido completo sin previo aviso.
Mainframe nórdico
va_arg se expande si es una macro y el ciclo while solo ejecuta la primera declaración, ¿quizás de muchas?
Maister
No (si eso sucediera, sería un error en la implementación).
zwol
5
EXPECT("## pow() gives exact results for integer arguments", pow(2, 4) == 16);

Otro es sobre el modo de texto en fopen. La mayoría de los programadores asumen que el texto y el binario son iguales (Unix) o que el modo de texto agrega \rcaracteres (Windows). Pero C ha sido portado a sistemas que usan registros de ancho fijo, en los que fputc('\n', file)en un archivo de texto significa agregar espacios o algo hasta que el tamaño del archivo sea un múltiplo de la longitud del registro.

Y aquí están mis resultados:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 en 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
dan04
fuente
De hecho, he visto código que se combina pow(2, n)con operaciones de bits.
dan04
4

Algunos de ellos no se pueden probar fácilmente desde dentro de C porque es probable que el programa se bloquee en las implementaciones donde la suposición no se cumple.


"Está bien hacer cualquier cosa con una variable con valor de puntero. Solo necesita contener un valor de puntero válido si lo desreferencia".

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 mismo con los tipos de punto flotante e integral (que no sean unsigned char), que pueden tener representaciones de trampa.


"Los cálculos de enteros terminan. Así que este programa imprime un entero negativo grande".

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

(C89 solamente.) "Está bien caerse al final de main".

#include <stdio.h>
int main () {
    puts("Hello.");
} /* The status code is 7 on many implementations. */
Gilles 'SO- deja de ser malvado'
fuente
2
Como ejemplo concreto: cuando se compila gcc -ftrapv -O, la salida es We like to think that:seguida porAborted
caf
@caf: "Esta opción genera trampas para el desbordamiento firmado en operaciones de suma, resta y multiplicación". Es bueno saberlo, gracias.
Gilles 'SO- deja de ser malvado'
1
El último también está bien en C ++ (98, 03 y 0x), e implícitamente devuelve 0.
jalf
Lo cual es desagradable porque la versión anterior a ANSI C permitía esto y C99 también.
Joshua
@Joshua: AFAIK no hay diferencia entre pre-ANSI C y C89 a cambio de mainsin valor: el programa es correcto pero devuelve un estado de terminación indefinido (C89 §2.1.2.2). Con muchas implementaciones (como gcc y compiladores de Unix más antiguos) obtienes lo que estaba en un registro determinado en ese momento. El programa generalmente funciona hasta que se utiliza en un archivo MAKE u otro entorno que verifica el estado de terminación.
Gilles 'SO- deja de ser malvado'
4

Bueno, los supuestos de portabilidad clásicos no mencionados aún son

  • supuestos sobre el tamaño de los tipos integrales
  • endianness
jk.
fuente
44
"Endianness", incluido "Hay un endianness": hay máquinas de endian medio, y el estándar permite cosas extrañas como almacenar un shortvalor fedcab9876543210 (que son 16 dígitos binarios) como los dos bytes 0248ace y fdb97531.
Gilles 'SO- deja de ser malvado'
sí, la endianess con seguridad incluye endian mixto / medio, así como grande y pequeño. Si va a un hardware personalizado, podría tener cualquier resistencia que desee en cualquier autobús.
jk.
El endian medio se conoce como PDP endian. Gilles describe algo aún más extraño que podría causar dolores de cabeza para implementar TCP / IP.
Joshua
@Gilles: de gama media ... Estoy muy contento de no estar desarrollando en eso. (pero ahora me pedirán que haga un proyecto de red de gama media, estoy seguro) ...
Paul Nathan
ARM FPE usó dobles de endian medio, donde se almacenaron como un par <high ​​quad> <low quad>, pero el orden de los bits dentro de cada quad era al revés. (Afortunadamente, ARM VFP ya no hace esto).
David dado el
4
  • Errores de discretización debido a la representación de coma flotante. Por ejemplo, si usa la fórmula estándar para resolver ecuaciones cuadráticas, o diferencias finitas para aproximar derivadas, o la fórmula estándar para calcular varianzas, se perderá precisión debido al cálculo de diferencias entre números similares. El algoritmo de Gauß para resolver sistemas lineales es malo porque los errores de redondeo se acumulan, por lo tanto, uno usa la descomposición QR o LU, la descomposición de Cholesky, SVD, etc. La adición de números de coma flotante no es asociativa. Hay valores denormales, infinitos y NaN. a + b - ab .

  • Cadenas: diferencia entre caracteres, puntos de código y unidades de código. Cómo se implementa Unicode en los diversos sistemas operativos; Codificaciones Unicode. No es posible abrir un archivo con un nombre de archivo Unicode arbitrario con C ++ de forma portátil.

  • Condiciones de carrera, incluso sin subprocesos: si prueba si existe un archivo, el resultado podría volverse inválido en cualquier momento.

  • ERROR_SUCCESS = 0

Philipp
fuente
4

Incluya un cheque para los tamaños enteros. La mayoría de la gente supone que un int es más grande que un short es más grande que un char. Sin embargo, todos estos podrían ser falsos:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Este código puede fallar (se bloquea el acceso no alineado)

unsigned char buf[64];

int i = 234;
int *p = &buf[1];
*p = i;
i = *p;
nos
fuente
¿fallaría este código en C ++? IIRC, es ilegal lanzar punteros entre tipos no relacionados, EXCEPTO para char *, que se puede lanzar a cualquier tipo (¿o es al revés?).
rmeador
1
Podrías hacerlo int *p = (int*)&buf[1];en c ++, la gente espera que eso también funcione.
nos
@nos, sí, eso puede fallar, pero el fallo es crash, por lo que su programa no puede probarlo. :(
Joshua
1
sizeof(char) < sizeof(int)es requerido. Por ejemplo, fgetc () devuelve el valor del carácter como un carácter sin signo convertido a int, o EOFque es un valor negativo. unsigned chares posible que no tenga bits de relleno, por lo que la única forma de hacerlo es haciendo que int sea más grande que char. Además, (la mayoría de las versiones de) la especificación C requiere que cualquier valor del rango -32767..32767 se pueda almacenar en un int.
jilles
@illes aún, hay DSP con caracteres de 32 bits y entradas de 32 bits.
nos
3

Un par de cosas sobre los tipos de datos integrados:

  • chary en signed charrealidad son dos tipos distintos (a diferencia inty signed intque se refieren al mismo tipo de entero con signo).
  • no se requieren enteros con signo para usar el complemento de dos. El complemento de unos y el signo + magnitud también son representaciones válidas de números negativos. Esto hace que las operaciones de bits que involucran números negativos sean definidas por la implementación .
  • Si asigna un número entero fuera de rango a una variable entera con signo, el comportamiento está definido por la implementación .
  • En C90, -3/5podría regresar 0o -1. El redondeo hacia cero en caso de que un operando fuera negativo solo se garantiza en C99 hacia arriba y C ++ 0x hacia arriba.
  • No hay garantías de tamaño exacto para los tipos incorporados. El estándar solo cubre requisitos mínimos, como un inttiene al menos 16 bits, un longtiene al menos 32 bits, un long longtiene al menos 64 bits. A floatpuede representar al menos 6 dígitos decimales más significativos correctamente. A doublepuede representar al menos 10 dígitos decimales más significativos correctamente.
  • IEEE 754 no es obligatorio para representar números de coma flotante.

Es cierto que en la mayoría de las máquinas tendremos dos complementos y flotadores IEEE 754.

sellibitze
fuente
Me pregunto qué valor tiene tener asignaciones enteras fuera de rango definidas por la implementación en lugar de comportamientos indefinidos. En algunas plataformas, tal requisito forzaría al compilador a generar código adicional para int mult(int a,int b) { return (long)a*b;}[por ejemplo, si intes 32 bits, pero se registra y longson 64]. Sin tal requisito, el comportamiento "natural" de la implementación más rápida de long l=mult(1000000,1000000);sería ligual a 1000000000000, aunque ese sea un valor "imposible" para un int.
supercat
3

Que tal este:

Ningún puntero de datos puede ser lo mismo que un puntero de función válido.

Esto es VERDADERO para todos los modelos planos, MS-DOS TINY, GRANDE y GRANDE, falso para el modelo MS-DOS PEQUEÑO, y casi siempre falso para los modelos MEDIO y COMPACTO (depende de la dirección de carga, necesitará un DOS muy antiguo para hazlo verdad).

No puedo escribir una prueba para esto

Y lo que es peor: se pueden comparar los punteros lanzados a ptrdiff_t. Esto no es cierto para el modelo MS-DOS LARGE (la única diferencia entre LARGE y HUGE es que HUGE agrega el código del compilador para normalizar los punteros).

No puedo escribir una prueba porque el entorno donde esta bomba dura no asignará un búfer mayor de 64K, por lo que el código que demuestra que se bloqueará en otras plataformas.

Esta prueba en particular pasaría en un sistema ahora difunto (tenga en cuenta que depende de lo interno de 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.");
Joshua
fuente
3

EDITAR: actualizado a la última versión del programa

Solaris-SPARC

gcc 3.4.6 en 32 bits

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 en 64 bits

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

y 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

y 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
revs tristopia
fuente
2

Puede usar text-mode ( fopen("filename", "r")) para leer cualquier tipo de archivo de texto.

Si bien , en teoría, esto debería funcionar bien, si también lo usa ftell()en su código, y su archivo de texto tiene finales de línea de estilo UNIX, en algunas versiones de la biblioteca estándar de Windows, ftell()a menudo devolverá valores no válidos. La solución es utilizar el modo binario en su lugar ( fopen("filename", "rb")).

Chinmay Kanchi
fuente
1

gcc 3.3.2 en AIX 5.3 (sí, necesitamos actualizar 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
chauncey
fuente
1

Una suposición que algunos pueden hacer en C ++ es que a structse limita a lo que puede hacer en C. El hecho es que, en C ++, a structes como unclass excepto que tiene todo público por defecto.

Estructura C ++:

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


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

protected:
  void Testing2();
};
Alerta
fuente
1

Las funciones matemáticas estándar en diferentes sistemas no dan resultados idénticos.

arsenm
fuente
1

Visual Studio Express 2010 en 32 bits x86.

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
Paul Nathan
fuente
1

Vía 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).

Tenga en cuenta que Codepad no tenía stddef.h. Eliminé la prueba 9 debido al teclado usando advertencias como errores. También cambié el nombre de la countvariable porque ya estaba definida por alguna razón.

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
Brian
fuente
1

¿Qué tal el desplazamiento a la derecha por cantidades excesivas? ¿Está permitido por el estándar o vale la pena probarlo?

¿El Estándar C especifica el comportamiento del siguiente programa:

vacío print_string (char * st)
{
  char ch;
  while ((ch = * st ++)! = 0)
    putch (ch); / * Supongamos que esto está definido * /
}
int main (nulo)
{
  print_string ("Hola");
  devuelve 0;
}

En al menos un compilador que uso, ese código fallará a menos que el argumento de print_string sea un "char const *". ¿La norma permite tal restricción?

Algunos sistemas permiten producir punteros a 'int' no alineados y otros no. Puede valer la pena probarlo.

Super gato
fuente
C89 §3.3.7: "Si el valor del operando derecho es negativo o es mayor o igual que el ancho en bits del operando izquierdo promovido, el comportamiento es indefinido". (se aplica a ambos <<y >>). C99 tiene un lenguaje idéntico en §6.5.7-3.
Gilles 'SO- deja de ser malvado'
Aparte de putch(¿por qué no usaste el estándar putchar?), No puedo ver ningún comportamiento indefinido en tu programa. C89 §3.1.4 especifica que "un literal de cadena de caracteres tiene [...] tipo 'array of char'" (nota: no const), y que "si el programa intenta modificar un literal de cadena [...], el comportamiento es indefinido" . ¿Qué compilador es ese y cómo traduce este programa?
Gilles 'SO- deja de ser malvado'
2
En C ++ las constantes de caracteres no son char [], son const char []. Sin embargo ... solía haber un agujero específico en el sistema de tipos para permitirle usar una constante de cadena en un contexto donde se esperaba un char * y no obtener un error de tipo. Esto condujo a situaciones en las que print_string ("foo") funcionaría pero print_string ("foo" +0) no. Esto fue profundamente confuso, especialmente en entornos donde los archivos C se compilan utilizando un compilador C ++ de forma predeterminada. El agujero se ha eliminado en nuevos compiladores, pero todavía hay muchos viejos alrededor. AFAIK C99 todavía define las constantes de cadena como char [].
David dado el
1
En los compiladores HiTech para la serie de controladores Microchip PIC, un puntero sin un calificador de almacenamiento solo puede apuntar a RAM. Un puntero calificado const puede apuntar a RAM o ROM. Los punteros no calificados const se desreferencian directamente en el código; los punteros calificados const se desreferencian a través de la rutina de la biblioteca. Dependiendo del tipo particular de PIC, los punteros no calificados constantes son de 1 o 2 bytes; los const-qualified son 2 o 3. Dado que la ROM es mucho más abundante que la RAM, tener constantes en ROM es generalmente una buena cosa.
supercat
@David dado: tenga en cuenta mi comentario anterior también. Prefiero compiladores que usan calificadores distintos de "const" para denotar la clase de almacenamiento de hardware; el compilador HiTech tiene algunas peculiaridades bastante molestas con su asignación de clase de almacenamiento (por ejemplo, los elementos de datos cuyo "tamaño de componente" es un byte, o los elementos de datos que tienen más de 256 bytes, van en un segmento "grande". Otros elementos de datos van en el " segmento "bss" para el módulo en el que están definidos; todos los elementos "bss" en un módulo deben caber dentro de 256 bytes. Las matrices que son ligeramente inferiores a 256 bytes pueden ser una verdadera molestia.
supercat
0

Para su información, para aquellos que tienen que traducir sus habilidades en C a Java, aquí hay algunas trampas.

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

En Java, char es de 16 bits y está firmado. El byte es de 8 bits y está firmado.

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

siempre es de 64 bits, las referencias pueden ser de 32 o 64 bits (si tiene más de una aplicación con más de 32 GB), las JVM de 64 bits suelen utilizar referencias de 32 bits.

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

El cambio está enmascarado para que yo << 64 == i == i << -64, i << 63 == i << -1

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

ByteOrder.nativeOrder () puede ser BIG_ENDIAN o LITTLE_ENDIAN

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

i = i++ nunca cambia i

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

El tamaño de las colecciones y las matrices siempre es de 32 bits, independientemente de si la JVM es de 32 bits o de 64 bits.

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 es de 16 bits, short es de 16 bits, int es de 32 bits y long es de 64 bits.

Peter Lawrey
fuente