Что принадлежит образовательному инструменту, чтобы продемонстрировать необоснованные предположения, которые люди делают в C/C++?
Я хотел бы подготовить небольшой обучающий инструмент для SO, который должен помочь начинающим (и средним) программистам распознавать и оспаривать их необоснованные предположения в C, C++ и их платформах.
Примеры:
- "целые числа обернуть"
- "У каждого есть ASCII"
- "Я могу хранить указатель на функцию в пустоте *"
Я подумал, что небольшая тестовая программа может быть запущена на различных платформах, которая использует "правдоподобные" допущения, которые, исходя из нашего опыта в SO, обычно делаются многими неопытными / полуопытными мейнстримными разработчиками и фиксируют способы их взлома на разных машинах.
Цель этого состоит не в том, чтобы доказать, что "что-то" безопасно делать (что было бы невозможно сделать, тесты доказывают только что-либо, если они сломались), а вместо этого продемонстрировать даже самому непонимающему человеку, как наиболее незаметно выражать его. сломаться на другом компьютере, если он имеет неопределенное или определяемое реализацией поведение.,
Для этого я хотел бы попросить вас:
- Как можно улучшить эту идею?
- Какие тесты будут хорошими и как они должны выглядеть?
- Будете ли вы запускать тесты на платформах, которые вы можете получить и опубликовать результаты, чтобы мы получили базу данных платформ, как они отличаются и почему это различие допустимо?
Вот текущая версия тестовой игрушки:
#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 тогда.
23 ответа
Порядок оценки подвыражений, в том числе
- аргументы вызова функции и
- операнды операторов (например,
+
,-
,=
,*
,/
), за исключением:- двоичные логические операторы (
&&
а также||
), - троичный условный оператор (
?:
), а также - оператор запятой (
,
)
- двоичные логические операторы (
не указано
Например
int Hello()
{
return printf("Hello"); /* printf() returns the number of
characters successfully printed by it
*/
}
int World()
{
return printf("World !");
}
int main()
{
int a = Hello() + World(); //might print Hello World! or World! Hello
/** ^
|
Functions can be called in either order
**/
return 0;
}
SDCC 29,7 / UCSIM / Z80
We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
From what I can say with my puny test cases, you are Stop at 0x0013f3: (106) Invalid instruction 0x00dd
printf вылетает. "О_О"
gcc 4.4@x86_64-suse-linux
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 79% mainstream
gcc 4.4@x86_64-suse-linux (-O2)
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 82% mainstream
clang 2.7@x86_64-suse-linux
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 72% mainstream
open64 4.2.3@x86_64-suse-linux
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream
intel 11.1@x86_64-suse-linux
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream
Turbo C++ / DOS / Малая память
We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 81% mainstream
Turbo C++ / DOS / Medium Memory
We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..10 void* can store function pointers
but 'sizeof(void*)>=sizeof(void(*)())' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 78% mainstream
Turbo C++ / DOS / Compact Memory
We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..20 ptrdiff_t and size_t have the same size
but '(sizeof(ptrdiff_t)==sizeof(size_t))' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 75% mainstream
cl65 @ Commodore PET (вице-эмулятор)
http://i34.tinypic.com/2hh0zmc.png
Я буду обновлять их позже:
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, Windows 7 64bit
(должен быть скомпилирован как C++, потому что компилятор CLR не поддерживает чистый C)
We like to think that:
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream
MINGW64 (предварительная версия gcc-4.5.2)
- 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
Давным-давно я преподавал C из учебника, в котором
printf("sizeof(int)=%d\n", sizeof(int));
как образец вопроса. Это не удалось для студента, потому что sizeof
дает значения типа size_t
не int
, int
на этой реализации было 16 бит и size_t
было 32, и это был big-endian. (Платформа была Lightspeed C на Macintosh на базе 680x0. Я сказал, что это было давно.)
Вы должны включить ++
а также --
предположения, которые люди делают.
a[i++]= i;
Например, синтаксически законно, но дает разные результаты в зависимости от слишком многих вещей, чтобы их рассуждать.
Любое утверждение, которое имеет ++
(или же --
) и переменная, которая встречается более одного раза, является проблемой.
Очень интересно!
Другие вещи, которые я могу придумать, могут быть полезны для проверки:
существуют ли указатели функций и указатели данных в одном и том же адресном пространстве? (Разрывы в машинах гарвардской архитектуры, таких как DOS small mode. Однако не знаю, как вы это проверите.)
если вы возьмете нулевой указатель данных и приведете его к соответствующему целочисленному типу, будет ли он иметь числовое значение 0? (Разрывы на некоторых действительно древних машинах - см. http://c-faq.com/null/machexamp.html.) То же самое с указателем на функцию. Также они могут иметь разные значения.
Увеличивает ли указатель после конца соответствующего ему объекта хранения, а затем обратно, ощутимые результаты? (Я не знаю ни одной машины, на которой это может быть взломано, но я полагаю, что спецификация C не позволяет вам даже думать об указателях, которые не указывают ни на (a) содержимое массива, ни (b) на элемент сразу после массива или (c) NULL. См. http://c-faq.com/aryptr/non0based.html.)
Приведет ли сравнение двух указателей к разным объектам хранения с помощью <и> согласованных результатов? (Я могу представить себе это нарушение на экзотических машинах на основе сегментов; спецификация запрещает такие сравнения, поэтому компилятор будет иметь право сравнивать только смещенную часть указателя, а не часть сегмента.)
Хм. Я постараюсь придумать еще.
Изменить: Добавил несколько поясняющих ссылок на превосходный C FAQ.
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
Вот забавный: что не так с этой функцией?
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! Нак гур пбзцвире vf erdhverq abg gb gerng guvf nf n pbzcvyr-gvzr reebe. (TPP qbrf rzvg n jneavat, gubhtu.)]
Я думаю, что вы должны попытаться провести различие между двумя очень разными классами "неправильных" предположений. Хорошая половина (сдвиг вправо и расширение знака, ASCII-совместимое кодирование, память линейна, данные и функциональные указатели совместимы и т. Д.) Являются довольно разумными предположениями для большинства C-кодеров, и могут даже быть включены в стандарт если бы C разрабатывался сегодня, и если бы у нас не было устаревшего барахла IBM. Другая половина (вещи, связанные с псевдонимами памяти, поведением библиотечных функций, когда память ввода и вывода перекрывается, 32-битные предположения, подобные указателям, вписываются в int
или что вы можете использовать malloc
без прототипа это соглашение о вызовах идентично для функций с переменными и не переменными, ...) либо конфликтует с оптимизациями, которые хотят выполнять современные компиляторы, либо с переходом на 64-разрядные машины или другой новой технологией.
Некоторые из них не могут быть легко протестированы изнутри 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. */
Ошибки дискретизации из-за представления с плавающей запятой. Например, если вы используете стандартную формулу для решения квадратных уравнений, или конечные разности для приближенных производных, или стандартную формулу для вычисления отклонений, точность будет потеряна из-за вычисления различий между подобными числами. Алгоритм Гаусса для решения линейных систем плох, потому что накапливаются ошибки округления, поэтому используются разложение QR или LU, разложение Холецкого, SVD и т. Д. Добавление чисел с плавающей запятой не ассоциативно. Существуют денормальные, бесконечные и NaN-значения. a + b - a ≠ b.
Строки: разница между символами, кодовыми точками и единицами кода. Как Unicode реализован в различных операционных системах; Unicode-кодировки Открытие файла с произвольным именем файла Unicode невозможно в C++ в переносимом виде.
Условия гонки, даже без многопоточности: если вы проверите, существует ли файл, результат может стать недействительным в любое время.
ERROR_SUCCESS
= 0
Ну, классические предположения о переносимости, которые еще не были
- предположения о размере целочисленных типов
- порядок байт
Включите проверку на целочисленные размеры. Большинство людей предполагают, что 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;
Как насчет этого:
Указатель на данные никогда не может быть таким же, как действительный указатель на функцию.
Это ИСТИННО для всех плоских моделей, моделей MS-DOS TINY, LARGE и HUGE, false для модели MS-DOS SMALL и почти всегда false для моделей 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.");
Несколько вещей о встроенных типах данных:
char
а такжеsigned char
на самом деле два разных типа (в отличие отint
а такжеsigned int
которые ссылаются на тот же тип целого числа со знаком).- целые числа со знаком не обязаны использовать дополнение до двух. Дополнение и знак + величина также являются действительными представлениями отрицательных чисел. Это делает битовые операции с участием отрицательных чисел определяемыми реализацией.
- Если вы назначите целое число вне диапазона для целочисленной переменной со знаком, поведение будет определяться реализацией.
- В С90
-3/5
мог вернуться0
или же-1
, Округление до нуля в случае, если один операнд был отрицательным, гарантируется только в C99 вверх и C++0x вверх. - Нет точных гарантий размера для встроенных типов. Стандарт охватывает только минимальные требования, такие как
int
имеет не менее 16 бит,long
имеет не менее 32 бит,long long
имеет не менее 64 бит.float
может по крайней мере правильно представить 6 наиболее значимых десятичных цифр.double
может по меньшей мере правильно представить 10 наиболее значимых десятичных цифр. - IEEE 754 не является обязательным для представления чисел с плавающей запятой.
Следует признать, что на большинстве машин у нас будет два дополнения и IEEE 754 с плавающей точкой.
РЕДАКТИРОВАТЬ: Обновлено до последней версии программы
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
Вы можете использовать текстовый режим ( fopen("filename", "r")
) читать любой текстовый файл.
Хотя в теории это должно работать просто отлично, если вы также используете ftell()
в вашем коде, и ваш текстовый файл имеет окончания строки в стиле UNIX, в некоторых версиях стандартной библиотеки Windows, ftell()
часто будет возвращать недопустимые значения. Решение состоит в том, чтобы использовать двоичный режим вместо (fopen("filename", "rb")
).
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
Предположение, что некоторые могут сделать в C++, заключается в том, что struct
ограничено тем, что он может делать в C. Дело в том, что в C++ struct
это как class
кроме того, что по умолчанию все общедоступно.
C++ структура:
struct Foo
{
int number1_; //this is public by default
//this is valid in C++:
private:
void Testing1();
int number2_;
protected:
void Testing2();
};
Как насчет смещения вправо чрезмерным количеством - это разрешено стандартом или стоит проверить?
В стандарте C указано поведение следующей программы:
void print_string (char * st) { char ch; while ((ch = * st ++)! = 0) Putch (ч); /* Предположим, что это определено */ } int main(void) { print_string("Hello"); вернуть 0; }
По крайней мере, на одном компиляторе, который я использую, этот код потерпит неудачу, если аргумент для print_string не является "char const *". Допускает ли стандарт такое ограничение?
Некоторые системы позволяют создавать указатели на невыровненные значения типа int, а другие - нет. Может стоит попробовать.
Через http://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
Стандартные математические функции в разных системах не дают одинаковых результатов.
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
К вашему сведению, для тех, кто должен перевести свои навыки 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 == 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-битный.