什么属于一个教育工具来演示人们在C / C ++中做出的无理假设?

我想为SO编写一个小小的教育工具,帮助初学者(和中级)程序员认识并挑战他们在C,C ++及其平台上的无理假设。

例子:

  • “整数环绕”
  • “每个人都有ASCII”
  • “我可以将函数指针存储在void *中

我认为可以在各种平台上运行一个小型的testing程序,这个程序运行“合理”的假设,即从我们的经验来看,通常是由许多缺乏经验/半经验的主stream开发人员制作的,并logging他们在不同机器上打破的方式。

这样做的目的并不是要certificate做某件事是“安全的”(这是不可能的事,testing只有在事件发生的时候才能certificate),而是要向最不能理解的人展示最不显眼的expression如果它有未定义的或实现定义的行为,则在不同的机器上中断。

为了达到这个目的,我想问你:

  • 这个想法如何改进?
  • 哪些testing是好的,他们应该是什么样子?
  • 你会在可以接触到的平台上运行testing并发布结果,以便我们得到平台数据库,它们有何不同以及为什么允许这种差异?

以下是testing玩具的当前版本:

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

呵呵,我从一开始就把这个社区维基做了,因为我觉得人们在阅读这篇文章时想编辑我的空话。

更新感谢您的意见。 我已经从你的答案中添加了几个例子,并会看看我是否可以像Gregbuild议的那样为此设置一个github。

更新 :我已经为此创build了一个github回购,该文件是“gotcha.c”:

  • http://github.com/lutherblissett/disenchanter

请在这里回答补丁或新的想法,所以可以在这里讨论或澄清。 然后我将它们合并成gotcha.c。

子expression式的评估顺序包括

  • 函数调用的参数
  • 运算符的操作数(例如+-=*/ ),但是:
    • 二进制逻辑运算符( &&|| ),
    • 三元条件运算符( ?: :)和
    • 逗号运算符( ,

未指定的

例如

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

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崩溃。 “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 /小内存

 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 /中等内存

 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 / Compact Memory

 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(副模拟器)

替代文字160wpb4.png


我将在稍后更新这些内容:


Windows XP上的Borland C ++ Builder 6.0

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

(必须编译为C ++,因为CLR编译器不支持纯C)

 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(gcc-4.5.2 prerelase)

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 

64位Windows使用LLP64模型: intlong都被定义为32位,这意味着对于指针来说都不够长。


avr-gcc 4.3.2 / ATmega168(Arduino Diecimila)

失败的假设是:

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

Atmega168有一个16位的PC,但代码和数据在不同的地址空间。 更大的Atmegas有一个22位的PC!


gcc 4.2.1在MacOSX 10.6上,用-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 

很久以前,我从一本教科书中教C

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

作为示例问题。 这对学生来说是失败的,因为在这个实现中, sizeof值为size_t ,而不是intint值是16位, size_t是32,而且是big-endian。 (这个平台是基于680×0的Macintoshes上的Lightspeed C,我说了很久以前了。)

你需要包括人们所做的++--假设。

 a[i++]= i; 

例如,在语法上是合法的,但是根据太多的事情产生不同的结果。

任何具有++ (或-- )和多次出现的variables的语句都是一个问题。

很有意思!

其他的事情,我可以想到它可能是有用的检查:

  • 函数指针和数据指针是否存在于相同的地址空间? (打破哈佛架构机器的DOS小模式,不知道如何testing它)

  • 如果你拿一个NULL数据指针并将其转换为合适的整数types,它是否具有数值0? (打破了一些真正古老的机器—请参阅http://c-faq.com/null/machexamp.html 。)同上的函数指针。 而且,它们可能是不同的值。

  • 增加一个指针超过其相应的存储对象的末尾,然后再回来,导致明智的结果? (我不知道任何机器实际上打破了,但我相信C规范不允许你考虑指针不指向(a)数组的内容或(b)元素紧接在数组之后或(c)NULL。参见http://c-faq.com/aryptr/non0based.html 。)

  • 使用<和>来比较两个指向不同存储对象的指针是否会产生一致的结果? (我可以想象,这会打破基于异种片段的机器;规范禁止这种比较,所以编译器将有权比较指针的偏移量部分,而不是段部分。)

嗯。 我会试着想一些。

编辑:增加了一些澄清的链接到优秀的C FAQ。

我认为你应该努力区分两种截然不同的“错误”假设。 一个好的一半(右移和符号扩展,ASCII兼容编码,内存是线性的,数据和函数指针是兼容的等等)对于大多数 C编码器来说是非常合理的假设,甚至可以作为标准的一部分如果C今天正在devise,如果我们没有旧的IBM垃圾邮件进入。 另一半(与内存别名相关的东西,当input和输出内存重叠时,库函数的行为,像指针那样的32位假设适合int或者你可以在没有原型的情况下使用malloc ,调用约定对于可变参数和非可变参数是相同的 – variables函数,…)与现代编译器想要执行的优化或与64位机器或其他新技术的迁移相冲突。

那么经典的可移植性假设还没有说明

  • 关于积分types的大小的假设
  • 字节序
  • 由浮点表示造成的离散化错误。 例如,如果使用标准公式求解二次方程,或者使用有限差分来近似导数,或者使用标准公式来计算方差,则由于计算相似数之间的差异,精度将会丢失。 高斯algorithm解决线性系统是不好的,因为四舍五入误差积累,因此使用QR或LU分解,乔列斯基分解,奇异值分解等。浮点数的加法是不相关的。 有denormal,无限和NaN值。 a + bab

  • string:字符,代码点和代码单元之间的区别。 在各种操作系统上如何实现Unicode; Unicode编码。 用一个随便的Unicode文件名打开一个文件是不可能的。

  • 比赛条件,即使没有线程:如果您testing一个文件是否存在,结果可能随时失效。

  • ERROR_SUCCESS = 0

这是一个有趣的问题:这个function有什么问题?

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

[答案(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。)]

 EXPECT("## pow() gives exact results for integer arguments", pow(2, 4) == 16); 

另一个是关于fopen文本模式。 大多数程序员假设文本和二进制文件是相同的(Unix),或者文本模式添加了\r字符(Windows)。 但是C已经被移植到使用固定宽度logging的系统上,文本文件上的fputc('\n', file)意味着添加空格或其他东西,直到文件大小是logging长度的倍数。

这里是我的结果:

在x86-64上的gcc(Ubuntu 4.4.3-4ubuntu5)4.4.3

 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 

其中一些不能轻易地从C里面进行testing,因为程序可能会在假设不成立的实现上崩溃。


“可以用指针variables来做任何事情,只要包含一个有效的指针值就可以了。”

 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 */ } 

与积分和浮点types( unsigned char除外)相同,允许有陷阱表示。


“整数计算环绕,所以这个程序打印一个大的负整数。”

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

(仅限C89)“可以脱离main的末端”。

 #include <stdio.h> int main () { puts("Hello."); } /* The status code is 7 on many implementations. */ 

包含一个整数大小的检查。 大多数人认为一个整数大于一个整数大于一个整数。 但是,这些可能都是false: sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short) sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

此代码可能会失败(崩溃到未alignment的访问)

 unsigned char buf[64]; int i = 234; int *p = &buf[1]; *p = i; i = *p; 

关于内置数据types的一些事情:

  • charsigned char实际上是两个不同的types(不像intsigned int ,指的是相同的有符号整数types)。
  • 有符号整数不需要使用二进制补码。 个体的补码和符号+幅度也是负数的有效表示。 这使涉及负数的位操作实现定义
  • 如果将超出范围的整数分配给有符号的整数variables,则该行为是实现定义的
  • 在C90中, -3/5可以返回0-1 。 在一个操作数为负的情况下向零进行倒数仅在C99向上和C ++ 0x向上保证。
  • 内置types没有确切的大小保证。 该标准只涵盖最低限度的要求,如int 至less有 16位, long至less 32位, long long至less 64位。 float至less可以正确表示6位最重要的十进制数字。 一个double可以正确地代表10个最重要的十进制数字。
  • 表示浮点数的IEEE 754不是强制性的。

无可否认,在大多数机器上,我们将有两个补码和IEEE 754浮点数。

编辑:更新到该程序的最后一个版本

Solaris的SPARC

在32位gcc 3.4.6

 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 

64位gcc 3.4.6

 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 

SUNSTudio 11 32位

 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 

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

How about this one:

No data pointer can ever be the same as a valid function pointer.

This is TRUE for all flat models, MS-DOS TINY, LARGE, and HUGE models, false for MS-DOS SMALL model, and almost always false for MEDIUM and COMPACT models (depends on load address, you will need a really old DOS to make it true).

I can't write a test for this

And worse: pointers casted to ptrdiff_t may be compared. This not true for MS-DOS LARGE model (the only difference between LARGE and HUGE is HUGE adds compiler code to normalize pointers).

I can't write a test because the environment where this bombs hard won't allocate a buffer greater than 64K so the code that demonstrates it would crash on other platforms.

This particular test would pass on one now-defunct system (notice it depends on the internals of 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."); 

You can use text-mode ( fopen("filename", "r") ) to read any sort of text file.

While this should in theory work just fine, if you also use ftell() in your code, and your text file has UNIX-style line-endings, in some versions of the Windows standard library, ftell() will often return invalid values. The solution is to use binary mode instead ( fopen("filename", "rb") ).

gcc 3.3.2 on AIX 5.3 (yeah, we need to update 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 

An assumption that some may do in C++ is that a struct is limited to what it can do in C. The fact is that, in C++, a struct is like a class except that it has everything public by default.

C++ struct:

 struct Foo { int number1_; //this is public by default //this is valid in C++: private: void Testing1(); int number2_; protected: void Testing2(); }; 

Standard math functions on different systems don't give identical results.

Visual Studio Express 2010 on 32-bit 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 

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

Note that Codepad did not have stddef.h . I removed test 9 due to codepad using warnings as errors. I also renamed the count variable since it was already defined for some reason.

 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 

How about right-shifting by excessive amounts–is that allowed by the standard, or worth testing?

Does Standard C specify the behavior of the following program:

void print_string(char *st)
 {
  char ch;
  while((ch = *st++) != 0)
    putch(ch); /* Assume this is defined */
 }
int main(void)
 {
  print_string("Hello");
  返回0;
 }

On at least one compiler I use, that code will fail unless the argument to print_string is a "char const *". Does the standard permit such a restriction?

Some systems allow one to produce pointers to unaligned 'int's and others don't. Might be worth testing.

FYI, For those who have to translate their C skills to Java, here are a few gotchas.

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

In Java, char is 16-bit and signed. byte is 8-bit and signed.

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

long is always 64-bit, references can be 32-bit or 64-bit (if you have more than an app with more than 32 GB) 64-bit JVMs typically use 32-bit references.

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

The shift is masked so that i << 64 == i == i << -64, i << 63 == i << -1

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

ByteOrder.nativeOrder() can be BIG_ENDIAN or LITTLE_ENDIAN

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

i = i++ never changes i

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

The size of collections and arrays is always 32-bit regardless of whether the JVM is 32-bit or 64-bit.

 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 is 16-bit, short is 16-bit, int is 32-bit and long is 64-bit.