Chciałbym przygotować małe narzędzie edukacyjne dla SO, które powinno pomóc początkującym (i średnio zaawansowanym) programistom rozpoznać i zakwestionować ich nieuzasadnione założenia w C, C ++ i ich platformach.
Przykłady:
- „zawijanie liczb całkowitych”
- „każdy ma ASCII”
- „Mogę przechowywać wskaźnik funkcji w pustej przestrzeni *”
Doszedłem do wniosku, że mały program testowy można uruchomić na różnych platformach, zgodnie z "wiarygodnymi" założeniami, które z naszego doświadczenia w SO są zwykle poczynione przez wielu niedoświadczonych / częściowo doświadczonych programistów głównego nurtu i rejestrują sposoby ich łamania na różnych maszynach.
Celem tego nie jest udowodnienie, że można coś zrobić „bezpiecznie” (co byłoby niemożliwe, testy dowodzą tylko wszystkiego, jeśli się zepsują), ale zamiast tego zademonstrowanie nawet najbardziej niezrozumiałej osobie, jak najbardziej niepozorne wyrażenie przerwa na innym komputerze, jeśli ma niezdefiniowane lub zdefiniowane w implementacji zachowanie. .
Aby to osiągnąć, chciałbym Cię zapytać:
- Jak można ulepszyć ten pomysł?
- Które testy byłyby dobre i jak powinny wyglądać?
- Czy przeprowadziłbyś testy na platformach, na których możesz dostać, i opublikował wyniki, abyśmy otrzymali bazę danych platform, czym się różnią i dlaczego ta różnica jest dozwolona?
Oto aktualna wersja zabawki testowej:
#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;
}
Aha, i stworzyłem tę wspólnotową wiki od samego początku, ponieważ pomyślałem, że ludzie chcą edytować moją paplaninę, kiedy to czytają.
AKTUALIZACJA Dzięki za wkład. Dodałem kilka przypadków z twoich odpowiedzi i zobaczę, czy mogę założyć github, jak zasugerował Greg.
AKTUALIZACJA : W tym celu utworzyłem repozytorium github, plik to „gotcha.c”:
Prosimy o przesłanie tutaj poprawek lub nowych pomysłów, aby można je było omówić lub wyjaśnić tutaj. W takim razie połączę je w gotcha.c.
dlsym()
zwraca void *, ale jest przeznaczony zarówno dla wskaźników danych, jak i funkcji. Dlatego poleganie na tym może nie być takie złe.