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