什么属于一个教育工具来演示人们在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模型: int
和long
都被定义为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
,而不是int
, int
值是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 + b – a ≠ b 。
-
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的一些事情:
-
char
和signed char
实际上是两个不同的types(不像int
和signed 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.