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


121

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

Примеры:

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

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

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

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

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

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

#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 для этого, как предложил Грег.

ОБНОВЛЕНИЕ : Я создал для этого репозиторий на github, файл - gotcha.c:

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


7
Рассмотрим среднюю модель в DOS. Функции могут храниться в нескольких сегментах, поэтому длина указателя функции составляет 32 бита. Но ваши данные хранятся только в одном сегменте, поэтому указатели данных имеют длину всего 16 бит. Поскольку void * - это указатель данных, он имеет ширину 16 бит, поэтому вы не можете поместить указатель на функцию в один. См. C-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
Дэвид Гивен

6
Возможно, вы могли бы разместить этот код на github.com или что-то в этом роде, и тогда люди могли бы легко вносить патчи.
Грег Хьюгилл,

1
Здесь многое должно помочь: stackoverflow.com/questions/367633/…
Мартин Йорк,

4
POSIX требует, чтобы указатели функций имели то же представление, что и void *, и могли быть преобразованы (с приведением) без потери информации. Одна из причин этого заключается в том, что он dlsym()возвращает void *, но предназначен как для указателей данных, так и для указателей функций. Поэтому может быть не так уж плохо зависеть от этого.
jilles

3
@tristopia: точка 15 здесь, потому что многие новички часто удивляются, узнав, что данные не упаковываются непрерывно, а вместо этого выровнены по определенным границам. Они недоумевают, когда меняют порядок членов и получают объекты разных размеров. Кроме того, упаковка является режимом по умолчанию для многих современных микроконтроллеров или встроенных устройств. Мой AVR Atmega и выход TurboC / MSDOS тоже упакованы. MSDOS по-прежнему используется в промышленных приложениях.
Nordic Mainframe

Ответы:


91

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

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

не указано

Например

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

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

3
@Billy: Но только для примитивных версий операторов.
Деннис Зикефуз

1
@ Деннис: Это правда. (Вот почему в Effective / MoreEffective C ++ есть пункт, чтобы никогда не перегружать их (если вы не пишете boost::spirit)
Билли Онил,

1
@ Даниэль: Я не уверен, что вы пытаетесь сказать. Похоже, вы предлагаете перегрузить операторы - это нормально, потому что только пользователи вашего класса могут ошибаться, и если вы пишете не на чистом C ++, это не имеет значения. Ни то, ни другое не имеет никакого смысла.
Деннис Зикефуз,

2
@ user420536: поведение не определено, но не определено. Да, пример может напечатать либо Hello World! или мир! Здравствуйте, но это просто не указано, потому что порядок оценки операндов +оператора не указан (разработчикам компилятора не нужно документировать поведение). Это не нарушает никаких правил точки последовательности как таковых.
Prasoon Saurav

38

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 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 / Компактная память

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 (вице-эмулятор)

альтернативный текст


Я обновлю их позже:


Borland C ++ 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 C ++ CLR, 64-разрядная версия Windows 7

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

- 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


32
И вы определили еще одно предположение: в терминальной строке можно уместить 80 символов.
Майк Сеймур,

3
sizeof(void*)>=sizeof(void(*)())будет более актуальным, чем ==. Все мы заботимся о том , «мы можем хранить указатель на функцию в пустой указатель», так что предположение вам нужно проверить , является ли void*это по крайней мере , столь же большой как указатель на функцию.
jalf

1
Если ваша среда совместима с POSIX, все должно быть в порядке sizeof(void*)>=sizeof(void(*)())- см. Opengroup.org/onlinepubs/009695399/functions/dlsym.html
Дэниел Эрвикер,

26

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

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

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


7
+1 за указание на одну из самых распространенных и часто упускаемых из виду ошибок такого рода.
R .. GitHub НЕ ПОМОГАЕТ ICE

4
Это также происходит в 64-битных системах, где size_t - 64 бит, а целые числа почти всегда короче. Win64 все еще более странный, потому что size_t unsigned long longтам. Добавлено как Test 17.
Nordic Mainframe

К сожалению, среда выполнения Microsoft C не поддерживает zмодификатор для size_tцелых чисел размера, а также long longне поддерживается на некоторых платформах. Таким образом, не существует безопасного переносимого способа форматирования или преобразования напечатанного размера объекта.
Фил Миллер

15

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

a[i++]= i;

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

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


И это тоже такой частый вопрос!
Matthieu M.

8

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

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

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

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

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

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

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

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


2
Между прочим, некоторое время назад я сделал экспериментальный проект под названием Clue ( cluecc.sourceforge.net ), который позволил вам скомпилировать C в Lua, Javascript, Perl, LISP и т. Д. Он безжалостно использовал неопределенное поведение в стандарте C, чтобы заставить указатели работать , Может быть интересно попробовать на нем этот тест.
Дэвид Гивен

1
IIRC C позволяет увеличивать указатель на 1 за пределами конца объекта, но не дальше. Однако его уменьшение до позиции перед началом объекта не допускается.
R .. GitHub НЕ ПОМОГАЕТ ICE

@Р. То же самое в C ++. И дальнейшее увеличение может прерваться, если увеличение указателя вызывает переполнение на процессорах, которые не просто обрабатывают указатели как целые числа.
jalf

5

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


это не просто «хлам IBM» (хотя я согласен, что материал IBM - хлам). Многие встраиваемые системы сегодня имеют похожие проблемы.
rmeador

Чтобы уточнить, использование mallocбез прототипа означает отказ от включения <stdlib.h>, что приводит mallocк установке по умолчанию « int malloc(int)нет-нет», если вы хотите поддерживать 64-разрядную версию.
Джои Адамс

Технически вы можете не включать, <stdlib.h>пока вы включаете другой заголовок, который определяет, size_tа затем mallocсами объявляете с правильным прототипом.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE

5

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

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


О, это хорошо. clang 2.7 это съедает и выдаёт полную чушь без предупреждения.
Nordic Mainframe,

va_arg расширяется, если это макрос, а цикл while выполняет только первый оператор из многих?
Maister

Нет (если бы это произошло, это была бы ошибка в реализации).
zwol

5
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

Я действительно видел код, сочетающийся pow(2, n)с битовыми операциями.
dan04,

4

Некоторые из них нелегко протестировать изнутри 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. */

2
В качестве конкретного примера: при компиляции с gcc -ftrapv -Oвыводом We like to think that:следуетAborted
caf

@caf: «Эта опция генерирует прерывания для подписанного переполнения при операциях сложения, вычитания и умножения». Приятно знать, спасибо.
Жиль 'SO- перестань быть злым'


Это неприятно, потому что до ANSI C это позволяло и C99 тоже.
Джошуа

@Joshua: AFAIK нет разницы между pre-ANSI C и C89 при возврате из mainбез значения: программа верна, но возвращает неопределенный статус завершения (C89 §2.1.2.2). Во многих реализациях (таких как gcc и более старые компиляторы unix) вы получаете все, что было в определенном регистре на тот момент. Программа обычно работает до тех пор, пока она не будет использована в make-файле или другой среде, которая проверяет статус завершения.
Жиль 'SO- перестань быть злым'

4

Что ж, классические допущения переносимости, которые еще не упоминаются,

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

4
«Порядок байтов», включая «Существует порядок байтов»: существуют машины с shortобратным порядком байтов, и стандарт разрешает такие странные вещи, как сохранение значения fedcab9876543210 (это 16 двоичных цифр) в виде двух байтов 0248ace и fdb97531.
Жиль 'SO- перестань быть злым'

да, порядок байтов наверняка включает смешанный / средний порядок байтов, а также большой и маленький. если вы перейдете на нестандартное оборудование, у вас может быть любой порядок байтов на любой шине.
jk.

Порядок байтов от среднего известен как PDP с порядком байтов. Жиль описывает кое-что еще более странное, хотя это вызовет головную боль при реализации TCP / IP.
Джошуа

@Gilles: с прямым порядком байтов ... Я очень рад, что не работаю над этим. (но теперь меня попросят сделать сетевой проект с прямым порядком байтов, я уверен) ...
Пол Натан,

В ARM FPE использовались числа с прямым порядком байтов, где они хранились как пара <high ​​quad> <low quad>, но порядок битов внутри каждого квадрата был неправильным. (К счастью, ARM VFP больше этого не делает.)
Дэвид Гивен,

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

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

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

  • ERROR_SUCCESS = 0


4

Включите проверку целочисленных размеров. Большинство людей полагают, что int больше, чем short, чем char. Однако все это может быть ложью: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;

не сработает ли этот код в C ++? IIRC, запрещено приводить указатели между несвязанными типами, ЗА ИСКЛЮЧЕНИЕМ для char *, который может быть приведен к любому типу (или это наоборот?).
rmeador

1
Вы можете просто сделать это int *p = (int*)&buf[1];на С ++, люди ожидают, что это тоже сработает.

@nos, да, это может привести к сбою, но сбой - это сбой, поэтому его программа не может проверить это. :(
Джошуа

1
sizeof(char) < sizeof(int)необходимо. Например, fgetc () возвращает значение символа как беззнаковый char, преобразованный в int, или EOFкоторый является отрицательным значением. unsigned charможет не иметь битов заполнения, поэтому единственный способ сделать это - сделать int больше, чем char. Кроме того, (большинство версий) спецификации C требуется, чтобы любое значение из диапазона -32767..32767 могло храниться в int.
jilles

@illes по-прежнему есть DSP с 32-битными символами и 32-битными int.

3

Пару вещей о встроенных типах данных:

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

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


Интересно, какое значение имеет назначение целочисленных значений вне диапазона, определяемых реализацией, а не неопределенное поведение? На некоторых платформах такое требование заставит компилятор сгенерировать дополнительный код для int mult(int a,int b) { return (long)a*b;}[например, if int- 32 бита, но регистры и long- 64]. Без такого требования «естественное» поведение самой быстрой реализации long l=mult(1000000,1000000);было бы установлено lравным 1000000000000, даже если это «невозможное» значение для int.
суперкар

3

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

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

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

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

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

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

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

3

РЕДАКТИРОВАТЬ: Обновлено до последней версии программы

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

2

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

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


1

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

1

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

Структура C ++:

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


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

protected:
  void Testing2();
};

1

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


1

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

1

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

1

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

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

void print_string (char * st)
{
  char ch;
  пока ((ch = * st ++)! = 0)
    Putch (ч); / * Предположим, что это определено * /
}
int main (пусто)
{
  print_string ( "Hello");
  возврат 0;
}

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

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


C89 §3.3.7: «Если значение правого операнда отрицательно или больше или равно ширине в битах продвинутого левого операнда, поведение не определено». (относится к обоим <<и >>). C99 имеет идентичный язык в §6.5.7-3.
Жиль 'SO- перестань быть злом'

Кроме putch(почему вы не использовали стандарт putchar?), Я не вижу неопределенного поведения в вашей программе. C89 §3.1.4 определяет, что «символьный строковый литерал имеет […] тип 'array of char'» (примечание: нет const) и что «если программа пытается изменить строковый литерал […], поведение не определено» , Что это за компилятор и как он транслирует эту программу?
Жиль 'SO- перестань быть злом'

2
В C ++ символьные константы - это не char [], это const char []. Однако ... раньше в системе типов была определенная дыра, позволяющая использовать строковую константу в контексте, где ожидалось char *, и не получать ошибку типа. Это привело к ситуациям, когда print_string ("foo") будет работать, а print_string ("foo" +0) - нет. Это сильно сбивало с толку, особенно в средах, где файлы C по умолчанию компилируются с использованием компилятора C ++. В новых компиляторах дыра была удалена, но старых осталось еще много. AFAIK C99 по-прежнему определяет строковые константы как char [].
Дэвид Гивен,

1
В компиляторах HiTech для контроллеров серии Microchip PIC указатель без квалификатора хранилища может указывать только на RAM. Указатель с квалификацией const может указывать либо на RAM, либо на ROM. Указатели, не квалифицированные константой, разыменовываются непосредственно в коде; Указатели с квалификацией const разыменовываются через библиотечную процедуру. В зависимости от конкретного типа PIC неконстантные указатели имеют размер 1 или 2 байта; с квалификацией const равны 2 или 3. Так как ПЗУ намного больше, чем ОЗУ, наличие констант в ПЗУ, как правило, - это хорошо.
supercat

@David Given: Обратите внимание и на мой предыдущий комментарий. Я предпочитаю компиляторы, которые используют квалификаторы, отличные от «const», для обозначения класса аппаратной памяти; компилятор HiTech имеет несколько досадных особенностей, связанных с распределением классов хранения (например, элементы данных, размер компонента которых составляет один байт, или элементы данных размером более 256 байт, помещаются в «большой» сегмент. Другие элементы данных помещаются в сегмент « bss "для модуля, в котором они определены; все элементы" bss "в модуле должны умещаться в пределах 256 байт. Массивы, размер которых немного меньше 256 байт, могут быть настоящей неприятностью.
supercat

0

К вашему сведению, для тех, кому нужно перевести свои навыки 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);

Сдвиг маскируется так, что i << 64 == i == i << -64, i << 63 == i << -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-битный, short - 16-битный, int - 32-битный, long - 64-битный.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.