Что входит в образовательный инструмент, чтобы продемонстрировать необоснованные предположения, которые люди делают на 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-разрядный.