Что входит в образовательный инструмент, чтобы продемонстрировать необоснованные предположения, которые люди делают на C/С++?

Я хотел бы подготовить небольшой учебный инструмент для SO, который должен помочь начинающим (и промежуточным) программистам распознавать и оспорить их необоснованные предположения на C, С++ и их платформах.

Примеры:

  • "целые числа вокруг"
  • "у всех есть ASCII"
  • "Я могу хранить указатель на функцию в void *"

Я подумал, что небольшую тестовую программу можно запускать на разных платформах, где выполняются "правдоподобные" предположения, которые из нашего опыта в SO, как правило, сделаны многими неопытными/полупрофессиональными разработчиками и записывают способы, которыми они разбиваются на разнообразные машины.

Цель этого не состоит в том, чтобы доказать, что "безопасно" что-то делать (что было бы невозможно сделать, тесты доказывают только что-либо, если они ломаются), а вместо этого демонстрируют даже самую непонятную индивидуальность как наиболее незаметное выражение разрывается на другой машине, если оно имеет undefined или поведение, определенное реализацией..

Чтобы добиться этого, я хотел бы спросить вас:

  • Как можно улучшить эту идею?
  • Какие тесты были бы хорошими и как они выглядели бы?
  • Не могли бы вы запустить тесты на платформах, на которых вы можете получить свои результаты, и опубликовать результаты, чтобы мы получили базу данных платформ, как они отличаются и почему эта разница разрешена?

Здесь текущая версия тестовой игрушки:

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

О, и я сделал эту вики сообщества с самого начала, потому что я решил, что люди хотят отредактировать мой блаббер, когда они прочтут это.

ОБНОВЛЕНИЕ Спасибо за ваш вклад. Я добавил несколько случаев из ваших ответов и увижу, могу ли я настроить github для этого, как предложил Грег.

UPDATE: Я создал для этого репозиторий github, файл "gotcha.c":

Пожалуйста, ответьте здесь патчами или новыми идеями, чтобы их можно было обсудить или разъяснить здесь. Затем я объединю их в gotcha.c.

Ответы

Ответ 1

Порядок оценки подвыражений, включая

  • аргументы вызова функции и
  • операнды операторов (например, +, -, =, *, /), за исключением:
    • двоичные логические операторы (&& и ||),
    • тернарный условный оператор (?:) и
    • оператор запятой (,)

Не указано

Например

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

Ответ 2


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. "О_О"


gcc [email protected]_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 [email protected]_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 [email protected]_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 [email protected]_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 [email protected]_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 С++/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 С++/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 С++/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 (вице-эмулятор)

alt text http://i34.tinypic.com/2hh0zmc.png


Я буду обновлять их позже:


Borland С++ Builder 6.0 в Windows XP

..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 С++ CLR, Windows 7 64bit

(должен быть скомпилирован как С++, потому что компилятор 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 preerase)

- 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-битный ПК, но код и данные находятся в отдельных адресных пространствах. У большего количества Atmegas есть 22-битный ПК!


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

Ответ 3

Давным-давно я преподавал C из учебника, в котором

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

как примерный вопрос. Это не удалось для ученика, потому что sizeof дает значения типа size_t, а не int, int для этой реализации было 16 бит, а size_t - 32, и это было большим. (Платформа была Lightspeed C на Macintoshes на базе 680x0. Я сказал, что это было давно.)

Ответ 4

Вам нужно включить предположения ++ и --, которые делают люди.

a[i++]= i;

Например, является синтаксически законным, но дает разные результаты, зависящие от слишком большого количества аргументов.

Любая инструкция, имеющая ++ (или --) и переменная, которая возникает более одного раза, является проблемой.

Ответ 5

Очень интересно!

Другие вещи, о которых я могу думать, могут быть полезны для проверки:

  • Существуют ли указатели на указатели и указатели данных в одном и том же адресном пространстве? (Перерывы в машинах архитектуры Гарварда, таких как малый режим DOS. Не знаю, как бы вы его протестировали.)

  • если вы берете указатель данных NULL и применяете его к соответствующему целочисленному типу, имеет ли оно числовое значение 0? (Перерывы на некоторых действительно древних машинах --- см. http://c-faq.com/null/machexamp.html.) То же самое с указателем на функцию. Кроме того, они могут быть разными значениями.

  • увеличивает ли указатель за концом соответствующего объекта хранения, а затем обратно, вызывает разумные результаты? (Я не знаю, какие машины на самом деле ломаются, но я считаю, что спецификация C не позволяет вам даже думать о указателях, которые не указывают ни на (a) содержимое массива, либо (b) элемент сразу после массива или (c) NULL. См. http://c-faq.com/aryptr/non0based.html.)

  • сравнивает два указателя с разными объектами хранения с < и > обеспечить согласованные результаты? (Я могу представить это разбиение на экзотических машинах на основе сегментов, спецификация запрещает такие сравнения, поэтому компилятор будет иметь право сравнивать только смещенную часть указателя, а не часть сегмента.)

Хм. Я попробую и подумаю о чем-то еще.

Изменить: Добавлены некоторые разъясняющие ссылки на отличный C FAQ.

Ответ 6

Я думаю, вы должны сделать попытку провести различие между двумя совершенно разными классами "неправильных" предположений. Хорошая половина (правая смена и расширение знака, ASCII-совместимое кодирование, память линейны, данные и указатели функций совместимы и т.д.) Являются довольно разумными предположениями для большинства C-кодировщиков, и могут даже быть включенными как часть стандарта, если C разрабатывался сегодня, и если бы у нас не было устаревшего IBM-мусора grandfathered-in. Другая половина (вещи, связанные с псевдонимом памяти, поведение библиотечных функций при перекрытии входной и выходной памяти, 32-разрядные предположения, подобные этим указателям, вписываются в int или что вы можете использовать malloc без прототипа, это соглашение вызова идентично для вариативных и невариантных функций,...) либо конфликтуют с оптимизациями, которые современные компиляторы хотят выполнить или с миграцией на 64-разрядные машины или другие новые технологии.

Ответ 7

  • Ошибки дискретизации из-за представления с плавающей запятой. Например, если вы используете стандартную формулу для решения квадратичных уравнений или конечных разностей для приближенных производных или стандартную формулу для вычисления дисперсий, точность будет потеряна из-за вычисления различий между похожими числами. Алгоритм Gauß для решения линейных систем плох, потому что скопления ошибок накапливаются, поэтому используется декомпозиция QR или LU, декомпозиция Холески, SVD и т.д. Добавление чисел с плавающей точкой не является ассоциативным. Существуют денормальные, бесконечные и NaN-значения. a + b - a ≠ b.

  • Строки: различие между символами, кодовыми точками и единицами кода. Как Unicode реализован в различных операционных системах; Кодировки Unicode. Открытие файла с произвольным именем Unicode невозможно с помощью С++ переносимым способом.

  • Условия гонки, даже без потоковой передачи: если вы проверяете, существует ли файл, результат может стать недействительным в любое время.

  • ERROR_SUCCESS= 0

Ответ 8

Хорошо, что классические предположения о переносимости, еще не обозначенные,

  • предположения о размере интегральных типов
  • порядок байт

Ответ 9

Вот забавный: что не так с этой функцией?

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

Ответ 10

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

Другой - это текстовый режим в fopen. Большинство программистов предполагают, что либо текст, и двоичный файлы одинаковы (Unix), или этот текстовый режим добавляет символы \r (Windows). Но C был перенесен в системы, в которых используются записи фиксированной ширины, на которых fputc('\n', file) в текстовом файле означает добавление пробелов или чего-либо до тех пор, пока размер файла не будет кратен длине записи.

И вот мои результаты:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 на x86-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.
..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

Ответ 11

Некоторые из них не могут быть легко протестированы изнутри C, потому что программа, скорее всего, потерпит крах в тех реализациях, где предположение не выполняется.


"Это нормально делать что-либо с переменной с указателем. Оно должно содержать только допустимое значение указателя, если вы его разыщите".

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

То же самое с целыми и плавающими типами (кроме 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. */

Ответ 12

EDIT: обновлена ​​до последней версии программы

Solaris SPARC-

gcc 3.4.6 в 32 бит

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

gcc 3.4.6 в 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.
..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

Ответ 13

Включить проверку для целых размеров.  Большинство людей полагают, что int  больше, чем короткий, больше, чем   char. Однако все это может быть  false: sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Этот код может выйти из строя (сбой к неравномерному доступу)

unsigned char buf[64];

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

Ответ 14

Несколько вещей о встроенных типах данных:

  • char и signed char на самом деле два разных типа (в отличие от int и signed int, которые относятся к одному и тому же типу со знаком целого).
  • целых чисел со знаком не требуется использовать два дополнения. Единицы дополнения и знак + величина также являются действительными представлениями отрицательных чисел. Это делает операции с битовыми операциями, связанными с определением отрицательных чисел.
  • Если вы назначаете целое число вне диапазона для целочисленной переменной со знаком, поведение определяется реализацией.
  • В C90 -3/5 может вернуться 0 или -1. Округление до нуля, если один операнд отрицательный, гарантируется только на C99 вверх и С++ 0x вверх.
  • Нет никаких точных гарантий размера для встроенных типов. Стандарт охватывает только минимальные требования, такие как int имеет не менее 16 бит, a long имеет не менее 32 бит, а long long имеет не менее 64 бит. A float может по крайней мере представлять 6 наиболее значимых десятичных цифр правильно. A double может по крайней мере представлять 10 наиболее значимых десятичных цифр правильно.
  • IEEE 754 не является обязательным для представления чисел с плавающей запятой.

По общему признанию, на большинстве машин у нас будет два дополнения и IEEE 754.

Ответ 15

Как насчет этого:

Никакой указатель данных не может быть таким же, как действительный указатель функции.

Это TRUE для всех моделей с плоским экраном, моделей MS-DOS TINY, LARGE и HUGE, ложных для модели MS-DOS SMALL и почти всегда false для моделей MEDIUM и COMPACT (в зависимости от адреса загрузки вам потребуется действительно старой DOS, чтобы сделать это правдой).

Я не могу написать тест для этого

И что еще хуже: указатели, отбрасываемые на ptrdiff_t, можно сравнить. Это неверно для модели MS-DOS LARGE (единственная разница между LARGE и HUGE - HUGE добавляет код компилятора для нормализации указателей).

Я не могу написать тест, потому что среда, в которой эти бомбы жестко не выделяет буфер больше, чем 64K, поэтому код, который демонстрирует его, будет сбой на других платформах.

Этот конкретный тест пройдет по одной ныне несуществующей системе (обратите внимание, что это зависит от внутренних элементов 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.");

Ответ 16

Вы можете использовать текстовый режим (fopen("filename", "r")) для чтения любого текстового файла.

В то время как это теоретически работает очень хорошо, если вы также используете ftell() в своем коде, а ваш текстовый файл имеет линии конца UNIX, в некоторых версиях стандартной библиотеки Windows ftell() часто возвращается недопустимые значения. Решение состоит в том, чтобы вместо этого использовать двоичный режим (fopen("filename", "rb")).

Ответ 17

gcc 3.3.2 на AIX 5.3 (да, нам нужно обновить 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

Ответ 18

Предположение, что некоторые могут делать в С++, состоит в том, что a struct ограничено тем, что он может сделать в C. Дело в том, что в С++ a struct похож на class, за исключением того, что он имеет все по умолчанию.

С++ struct:

struct Foo
{
  int number1_;  //this is public by default


//this is valid in C++:    
private: 
  void Testing1();
  int number2_;

protected:
  void Testing2();
};

Ответ 19

Стандартные математические функции в разных системах не дают одинаковых результатов.

Ответ 20

Visual Studio Express 2010 на 32-разрядной версии 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

Ответ 21

Через 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).

Обратите внимание, что у Codepad не было stddef.h. Я удалил тест 9 из-за кодекса, используя предупреждения как ошибки. Я также переименовал переменную count, поскольку она по какой-то причине была определена.

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

Ответ 22

Как насчет правильного смещения чрезмерных сумм - это разрешено стандартом или стоит проверить?

Указывает ли стандарт C поведение следующей программы:

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

По крайней мере один компилятор, который я использую, этот код завершится неудачно, если аргумент print_string не будет "char const *". Предоставляет ли такое разрешение такое ограничение?

Некоторые системы позволяют создавать указатели для unaligned 'int, а другие - нет. Возможно, стоит проверить.

Ответ 23

FYI. Для тех, кто должен перевести свои навыки C на Java, вот несколько ошибок.

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

В Java char 16-бит и подписан. байт 8-бит и подписан.

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

long всегда 64-разрядный, ссылки могут быть 32-разрядными или 64-разрядными (если у вас более одного приложения с более чем 32 ГБ), 64-разрядные JVM обычно используют 32-разрядные ссылки.

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

Смещение маскируется так, что я < 64 == я == я < -64, я < 63 == я < -1

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

ByteOrder.nativeOrder() может быть BIG_ENDIAN или LITTLE_ENDIAN

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

i = i++ никогда не изменяется i

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

Размер коллекций и массивов всегда 32-разрядный независимо от того, является ли JVM 32-разрядной или 64-разрядной версией.

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 - 16-разрядный, короткий - 16 бит, int 32-битный и длинный - 64-разрядный.