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