Я хотів би підготувати невеликий навчальний інструмент для ПЗ, який повинен допомогти початківцям (і проміжним) програмістам розпізнати та оскаржити свої необґрунтовані припущення на C, C ++ та їх платформах.
Приклади:
- "цілі числа обгортаються"
- "у всіх є ASCII"
- "Я можу зберігати покажчик функції у порожнечі *"
Я подумав, що невелика тестова програма може бути запущена на різних платформах, яка виконує "правдоподібні" припущення, які, з нашого досвіду роботи в SO, зазвичай робляться багатьма недосвідченими / напівдосвідченими основними розробниками та записують способи їх розбиття на різних машинах.
Мета цього - не довести, що "безпечно" щось робити (що було б неможливо зробити, тести доказують лише все, якщо вони ламаються), а натомість продемонструвати навіть самому непорозумілому людині, як найбільш непомітний вираз перерва на іншій машині, якщо вона має невизначене чи визначене реалізацією поведінку. .
Для цього я хотів би попросити вас:
- Як цю ідею можна вдосконалити?
- Які тести були б хорошими і як вони повинні виглядати?
- Чи будете ви запускати тести на платформах, ви можете отримати свої руки та опублікувати результати, щоб ми закінчилися з базою даних платформ, чим вони відрізняються і чому така різниця дозволена?
Ось поточна версія для тестової іграшки:
#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;
}
О, і я зробив цю спільноту вікі з самого початку, тому що я зрозумів, що люди хочуть редагувати мій blaber, коли вони читають це.
ОНОВЛЕННЯ Дякуємо за ваш внесок. Я додав кілька випадків із ваших відповідей і побачу, чи зможу я створити github для цього, як запропонував Грег.
ОНОВЛЕННЯ : Я створив для цього github repo, файл "gotcha.c":
Будь ласка, відповідайте тут патчами чи новими ідеями, щоб їх можна було обговорити чи уточнити тут. Я злитлю їх у gotcha.c тоді.
dlsym()
повернення порожнечі *, але призначене як для даних, так і для функціональних покажчиків. Тому може бути не так вже й погано залежати від цього.