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

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

例子:

  • “整数环绕”
  • “每个人都有ASCII”
  • “我可以将函数指针存储在void *中”
  • 我想,一个小的测试程序可以在各种平台上,运行于“似是而非”的假设它们是,从我们的经验,所以,平时很多没有经验/ semiexperienced主流开发商提出并记录他们在不同的机器坏了的方式运行。

    这样做的目的不是证明它是“安全”做一些事情(这是不可能做到的,测试只能证明什么,如果他们打破),而是以证明即使是最不理解的人是怎样最不起眼的表达如果它有未定义的或实现定义的行为,则在不同的机器上中断。

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

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

  • 以下是测试玩具的当前版本:

    #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("..%sn   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%% mainstreamn",100-(100*count)/total);
        return 0;
    }
    

    哦,我从一开始就让这个社区维基正确,因为我认为人们在阅读时想编辑我的空话。

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

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

  • http://github.com/lutherblissett/disenchanter
  • 请在这里回答补丁或新想法,以便在此讨论或澄清。 然后我将它们合并为gotcha.c。


    子表达式的评估顺序,包括

  • 函数调用的参数
  • 运算符的操作数(例如, +-=*/ ),但以下情况除外:
  • 二进制逻辑运算符( &&|| ),
  • 三元条件运算符( ?: :)和
  • 逗号运算符( ,
  • 未指定

    例如

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

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

    替代文字http://i34.tinypic.com/2hh0zmc.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!


    在MacOSX 10.6上的gcc 4.2.1,用-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)=%dn", sizeof(int));
    

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

    链接地址: http://www.djcxy.com/p/12727.html

    上一篇: What belongs in an educational tool to demonstrate the unwarranted assumptions people make in C/C++?

    下一篇: Undefined behavior and sequence points reloaded