[c++] 사람들이 C / C ++에서하는 부당한 가정을 보여주는 교육 도구에는 무엇이 포함됩니까?

저는 초보자 (및 중급) 프로그래머가 C, C ++ 및 플랫폼에서 정당하지 않은 가정을 인식하고 이의를 제기하는 데 도움이되는 SO를위한 작은 교육 도구를 준비하고 싶습니다.

예 :

  • “정수를 둘러싼 다”
  • “모든 사람이 ASCII를 가지고 있습니다.”
  • “함수 포인터를 void *에 저장할 수 있습니다.”

저는 소규모 테스트 프로그램이 다양한 플랫폼에서 실행될 수 있다고 생각했습니다.이 가정은 일반적으로 경험이 부족한 / 반 경험이없는 많은 주류 개발자가 만든 “그럴듯한”가정을 실행하고 다양한 시스템에서 중단되는 방식을 기록합니다.

이것의 목표는 무언가를하는 것이 “안전”하다는 것을 증명하는 것이 아니라 (불가능할 것이고, 테스트가 깨질 경우에만 어떤 것을 증명 함), 대신 가장 눈에 띄지 않는 표현을 가장 이해하기 어려운 개인에게 보여주기위한 것입니다. 정의되지 않았거나 구현 정의 된 동작이있는 경우 다른 시스템에서 중단됩니다. .

이를 달성하기 위해 다음과 같이 질문하고 싶습니다.

  • 이 아이디어를 어떻게 개선 할 수 있습니까?
  • 어떤 테스트가 좋으며 어떻게 표시되어야합니까?
  • 손을 잡고 결과를 게시 할 수있는 플랫폼에서 테스트를 실행하여 결과를 게시하여 플랫폼 데이터베이스로 끝낼 수 있으며, 서로 어떻게 다른지, 왜이 차이가 허용되는지?

테스트 장난감의 현재 버전은 다음과 같습니다.

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

아, 그리고 나는 사람들이 이것을 읽을 때 내 blabber를 편집하고 싶어한다고 생각했기 때문에 처음부터이 커뮤니티 위키를 만들었습니다.

업데이트 귀하의 의견에 감사드립니다. 귀하의 답변에서 몇 가지 사례를 추가했으며 Greg가 제안한 것처럼 github를 설정할 수 있는지 확인할 것입니다.

업데이트 : 나는 이것을 위해 github repo를 만들었습니다. 파일은 “gotcha.c”입니다.

여기에서 패치 또는 새로운 아이디어로 답변 해주세요. 여기에서 논의하거나 명확히 할 수 있습니다. 그런 다음 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

인텔 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

터보 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 / 컴팩트 메모리

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 (부스 에뮬레이터)

대체 텍스트


나중에 업데이트 할 예정입니다.


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 64 비트

(CLR 컴파일러는 순수 C를 지원하지 않으므로 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 프리 릴리즈)

-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 모델을 사용하여 비트 : 모두 intlong32 비트 수단으로 정의되는 어느 포인터에 대해 충분히입니다.


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가 있지만 코드와 데이터는 별도의 주소 공간에 있습니다. 더 큰 Atmega에는 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)=%d\n", sizeof(int));

샘플 질문으로. 때문에, 학생 실패 sizeof유형의 수율 값이 size_t아니라 int, int이 구현에 16 비트이고 size_t32이고, 그것은 빅 엔디안했다. (플랫폼은 680×0 기반 매킨토시의 Lightspeed C였습니다. 오래 전이라고 말했습니다.)


답변

사람들이 하는 ++--가정 을 포함해야합니다 .

a[i++]= i;

예를 들어,는 구문 적으로 합법적이지만 추론 할 수없는 항목이 너무 많으면 다양한 결과를 생성합니다.

++(또는 --)과 두 번 이상 발생하는 변수가있는 문 은 문제입니다.


답변

매우 흥미로운!

내가 생각할 수있는 다른 것들은 다음을 확인하는 데 유용 할 수 있습니다.

  • 함수 포인터와 데이터 포인터가 동일한 주소 공간에 있습니까? (DOS 스몰 모드와 같은 Harvard 아키텍처 머신에서 중단됩니다. 어떻게 테스트할지 모르겠습니다.)

  • NULL 데이터 포인터를 가져 와서 적절한 정수 유형으로 캐스트하면 숫자 값이 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가 현재 설계되고 있고 기존 IBM 정크가없는 경우. 나머지 절반 (메모리 앨리어싱과 관련된 것, 입력 및 출력 메모리가 겹칠 때 라이브러리 함수의 동작, 포인터가 맞 int거나 사용할 수 있는 것과 같은 32 비트 가정)malloc 프로토 타입이 없으면이 호출 규칙은 가변 및 비가 변 함수에 대해 동일합니다. …) 현대 컴파일러가 수행하려는 최적화와 충돌하거나 64 비트 시스템 또는 기타 새로운 기술로의 마이그레이션과 충돌합니다.


답변

여기에 재미있는 것이 있습니다.이 기능에 어떤 문제가 있습니까?

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