1. Пользуйтесь тегами кода. - [code] ... [/code] 2. Точно указывайте язык, название и версию компилятора (интерпретатора). 3. Название темы должно быть информативным. В описании темы указываем язык!!!
Итак, Новый Год, заняться особенно нечем, а давайте я вас чуть-чуть "подразню" что-ли? Покажу вам на простых примерах некоторые возможности одного из языков программирования, который многие считают устаревшим. Не вопрос, считайте дальше, на данный момент мы пользуемся Стандартом 2005-го года, т.е., новее, чем у С++, к 2012 готовится очередная версия Стандарта.
Но, собственно, я не собираюсь разжигать здесь холивар, максимум - пробудить интерес. Если кто-то заинтересуется - уже хорошо, если нет - то будем считать, что это все написано, чтоб провести время, не просто так смотря в монитор...
Итак.
С чего начнем? Наверное, с Hello World? Нет, не интересно. Напишем хоть сколько-нибудь полезную программку. Пускай она получает от пользователя число, и определяет, положительное оно или отрицательное:
(Паскаль)
var i: integer; begin write('i = '); readln(i);
if i > 0 then writeln('positive') else writeln('negative or zero'); end.
(Ада)
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Ada.Text_IO; use Ada.Text_IO; procedure Hello is i: integer; begin Put("i = "); Get(i);
if i > 0 then Put_Line ("positive"); else Put_Line("negative or zero"); end if;
end;
Что бросается в глаза? "Многословность". Похоже, это - единственный недостаток Ады
Давайте теперь немного поговорим о преимуществах.
0. Описание переменных по мере необходимости.(Показать/Скрыть)
Та вещь, которой я всегда завидовал в С++, и которая присутствует в Аде: описание переменных там, где они нужны, а не там, где описан заголовок функции/процедуры. В любом месте программы можно добавить
declare -- Описание переменной begin -- Ее использование end;
, после закрытия блока (после оператора end) переменная выходит из области видимости и попытка обратиться к ней приводит к ошибке компиляции.
1. Циклы For.(Показать/Скрыть)
Все, наверное, встречали типовую ошибку начинающего программиста (особенно это касается Турбо-Паскаля, где эта ошибка никак не отлавливается) - изменение управляющей переменной цикла в самом цикле. И еще одна: обращение к переменной цикла после его окончания. Ну, скажем:
var i: integer; begin for i := 1 to 10 do begin writeln(i); inc(i); end; end.
Знакомая картина, правда? Хотя в описании языка ясно сказано: подобное изменение - это ошибка! Чуть лучше дело обстоит в 32-битных компиляторах, там подобный код компилироваться не будет. Однако вариант языка Ada мне нравится еще больше:
procedure Hello is begin for i in 1 .. 10 loop -- i вообще не описывается Put(i); New_Line; end loop; end;
, то есть мало того, что i - вообще не описывается (в самом деле, зачем? Что, компилятор по типу индексов не сможет определить, какую переменную использовать для их перебора? Сможет...), а раз не описывается, то все вопросы типа "Что такое? Переменная есть, а изменить нельзя..." отпадают. Нет переменной и все тут так еще и видимость i ограничена только текущим блоком for loop ... end loop, то есть, обращение к ней после end-а вообще лишены смысла.
Кстати, для того, чтобы заставить компилятор сделать "обратный цикл" достаточно просто:
for i in reverse 1 .. 10 loop -- добавить reverse
2. Проверка логических условий.(Показать/Скрыть)
Вот еще одно слабое место Паскаль-программ:
if (условие_1) and (условие_2) then ...
Если условие_1 не выполняется, то при вычислении условия_2 может произойти ошибка времени исполнения (скажем, обращение по нулевому указателю или попытка деления на 0) при полном вычислении логических условий, поэтому для избежания подобных ошибок приходится "оборачивать" условия в директиву {$B-} ... {$B+}, чтобы гарантировать "быстрое" вычисление, при этом если условие_1 вернет false, то программа не станет вычислять условие_2, и, следовательно, ошибки не произойдет.
В Ada для решения подобной проблемы есть расширенный синтаксис оператора условия:
if (условие_1) and then (условие_2) then ...
, при этом вычисление условия_2 будет производиться только если условие_1 истинно. Для выражений, разделенных or, расширенный синтаксис используется в виде
if (условие_1) or else (условие_2) then ...
То есть, все делается средствами языка, никаких зависимостей от настроек среды или от версии компилятора...
3. Оператор Goto(Показать/Скрыть)
Какой основной довод приводят для того, чтобы оправдать использование goto? В частности - простой выход из нескольких вложенных циклов. Вот, например:
function f(i, j, k: integer): boolean; begin f := (i + 10*j - 3*k > 20); end;
var i, j, k: integer; label finish; begin for i := 1 to 10 do for j := 1 to 10 do for k := 1 to 10 do begin writeln(i + j + k); if f(i, j, k) then goto finish; end; finish:; end.
Если не использовать goto, то выйти из всех трех циклов через Break - невозможно, придется менять циклы на While или Repeat, и усложнять условия + добавлять еще флаги...
Ада предлагает другой вариант - именованные циклы и более мощные exit-ы:
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hello is function f(i, j, k: integer) return Boolean is begin return ((i + 10*j - 3*k) > 20); end;
begin first_loop: for i in 1 .. 10 loop for j in 1 .. 10 loop for k in 1 .. 10 loop Put(i + j + k); exit first_loop when f(i, j, k); end loop; end loop; end loop first_loop; Put_Line("Oops..."); end;
В Турбо Паскале ими и не пахнет, хотя создан был компилятор Ada-83 еще раньше чем финальные версии Турбо Паскаля. В 32-битах оно есть, но вот пример, как хочется их использовать, потому что это удобно:
procedure f(a: integer = 10; b: real = 20.0; s: string = 'Ok'); begin writeln(a:5, b:10:4, s:15); end;
begin f(); // Здесь все ясно: все параметры - по умолчанию f(11); // Здесь - меняется первый параметр, остальные - по умолчанию f(10, 14.0); // <--- Ну я же не хочу менять первый параметр, почему его надо повторять??? f(10, 20.0, 'Error'); // <--- А здесь повторяется уже 2 default-значения. Зачем? end.
Сравниваем:
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Hello is procedure f(a: Integer := 10; b: Float := 20.0; s: String := "Ok") is begin Put(a); Put(b); Put(s); New_Line; end;
begin f; -- f(11); -- f(b => 14.0); -- меняется только b f(s => "Error"); -- и только s ... end;
5. Инициализация массивов.(Показать/Скрыть)
Как, к примеру, описать массив целых из 100 элементов и заполнить его так, чтобы первые 22 элемента были тройками, потом еще 7 пятерок, а все остальные - четверки? Просто, правда? Или описать все значения явно (ну хорошо, а что делать если все умножить на 10, т.е., не 100 а тысяча элементов?) или сделать небольшой цикл (или три, в зависимости от настроения) который будет это заполнять. Но погодите, мы же используем не Паскаль... Ну-ка...
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Hello is
subtype arrIndex is Integer range 1 .. 100; type arrType is array(arrIndex) of Integer;
begin for i in arrIndex loop Put(arr(i)); end loop; end;
- еще на этапе трансляции... То же самое касается и присвоения значений записям и объектам. Кстати, вариантных полей (селекторов) в записи может быть несколько, в отличие от Паскаля, где все ограничено одним Case-полем.
6. Еще немного о массивах.(Показать/Скрыть)
Разумеется, все сталкивались с подобной проблемой. Допустим, вы передаете в подпрограмму размер буфера, и в самой подпрограмме вам нужен буфер именно такого размера. В таком случае пользователю Паскаля ничего не остается, как использовать динамические массивы:
Procedure P(size: Integer); Var Arr: array[1 .. size] of Integer; Begin // тут работаем с Arr End;
Это не будет компилироваться (даже если size описать так: Procedure P(Const size: Integer);). Нужно либо использовать Array of Integer + SetLength, либо GetMem или подобные средства выделения памяти. Что касается Ады:
procedure P(size: integer) is arr: array(1 .. size) of Integer; begin -- тут работаем с Arr end P;
это совершенно легальная конструкция, которая прекрасно работает - массив arr будет содержать ровно size элементов. Более того, можно сделать еще красивее (с использованием блока declare, о котором я упоминал выше):
-- здесь каким-то образом вычисляем -- или получаем от пользователя size declare -- описываем локальный для блока declare массив arr: array(1 .. size) of Integer; begin -- тут работаем с arr end; -- а тут массива уже не существует, и обращаться к нему нельзя ...
(чем не "сборка мусора"?)
7. Параметры подпрограмм.(Показать/Скрыть)
В Ada программист не должен описывать метод передачи данных в подпрограмму (то есть, передается ли параметр по значению, или по ссылке - Var, или по константной ссылке - Const), это решает компилятор, который прекрасно знает размеры всех типов (вот они, преимущества строгого описания типов) и сам решает, как передавать данные.
Программист задает только уровень доступа к параметрам: или только для чтения (in), или только для записи (out, при этом формальный параметр вообще не инициализируется значением фактического параметра, а фактический - получает значение, присвоенное формальному внутри процедуры), или аналог Var-параметров (in out, формальный параметр инициализируется фактическим, и все его изменения будут доступны "снаружи") или access-режим, введенный в стандарте Ada95 для передачи по ссылке.
При этом малейшая попытка изменить значение in-параметра (помните, в Паскале постоянная практика - параметр переданный по значению, можно менять, все равно его изменение назад не передается) карается прекращением компиляции. Это - параметр только для чтения. Менять его нельзя. Так же наказывается попытка выйти из подпрограммы без инициализации out-параметра. Если такой параметр есть - надо его инициализировать, иначе в вызывающем блоке могут начаться проблемы, чего Ада со своей повышенной безопасностью допустить не может.
Кстати, вот еще одно преимущество Ada: Это полностью стандартизированный язык. И такого, как происходит с Паскалем - "что хочу, то и ворочу" (в зависимости от компилятора) просто не может быть. В первую очередь программа должна соответствовать Стандарту, и если она ему соответствует, то гарантируется ее одинаковая работа на всех трансляторах.
8. Дженерики.(Показать/Скрыть)
Ада предоставляет программисту возможность использовать Дженерики. Причем то, что недавно стало возможным в FPC 2.2.0 - лишь жалкое подобие тех возможностей, которые есть в Ada. Ну, скажем, описание шаблонных функций, инициализация шаблонного пакета не типом, а некоторым значением или вообще функцией, что невозможно (будем надеяться, пока) в Паскале/Дельфи.
Немного о безопасности
Недавно мне задали вот такой (очень, казалось бы, простой) вопрос: "Везде, где написано про язык программирования Ада, есть утверждение, что он - более безопасный, чем тот же С/С++. А в чем это выражается?"
А давайте попробуем посмотреть в чем это выражается (в основном сравнение происходит с С-подобными языками, Паскаль может не иметь многих из нижеперечисленных недостатков)...
1. Пример программы на С
#include <stdio.h> int main () { int length = 8265; int width = 0252; int height = 8292; printf ("length = %d\n", length); printf ("width = %d\n", width); printf ("height = %d\n", height); }
Программа прекрасно компилируется и запускается на выполнение... Но каким будет ее вывод?
Вот таким будет вывод:(Показать/Скрыть)
length = 8265 width = 170 height = 8292
по той простой причине, что в С/С++ числа, начинающиеся с 0 трактуются как записанные в Octal (8-ричной системе счисления). Это очень трудно обнаруживается программистом, и является потенциальным источником ошибки.
Плюс к этому, если уже есть префиксы и для 16-ричной системы счисления (0x) и для 8-ричной (0), то почему забыта двоичная система? Как в С/С++ задать значение в двоичной системе? А в троичной?
Для сравнения:
with Text_IO; procedure Main is Length : Integer := 8265; Width : Integer := 0252; Height : Integer := 8292; begin Text_IO . Put_Line (Length'img); Text_IO . Put_Line (Width'img); Text_IO . Put_Line (Height'img); end Main;
не приводит ни к каким сюрпризам:
8265 252 8292
, ведущие нули просто игнорируются. Что касается задания числа в другой системе счисления - нет проблем, в любой, с основанием от 2 до 16:
-- можно отделять тысячи/миллионы/... друг от друга (для удобства программиста) Length : Integer := 8_265; -- обычное десятичное число Width : Integer := 0252; -- восьмеричное число Width_8 : Integer := 8#252#; -- двоичное число (можно разделять тетрады символом подчеркивания, для удобства) B_Mask : Integer := 2#1100_1011#; -- 16-ричное число W_Mask : Integer := 16#FFF1_A4B0#; -- Система счисления с основанием 7 Strange : Integer := 7#12345#;
2. Корректен ли следующий код:
#include <limits.h>
/* Если *y - ноль и x > 0, то *k приравнять максимальному положительному числу Если *y - ноль и x <= 0, оставить *k без изменений Если *y - не ноль, присвоить *k значение x деленного на *y, и увеличить *y на 1 */ void check_divide (int *k, int x, int *y) { if (*y = 0) if (x > 0) *k = INT_MAX; else *k = x / *y /* Здесь делить на *y безопасно, поскольку нулю оно не равно */; *y++; }
? Несмотря на то, что программа компилируется, здесь присутствуют как минимум 4 серьёзные проблемы. Какие?
...(Показать/Скрыть)
Проблема №1 Смешивание "=" и "==". "=" представляет собой присваивание, тогда как "==" - это сравнение. Очевидно, должно использоваться именно сравнение. Ну, а поскольку многие программисты отключают все Warning-и, то даже подсказка компилятора останется незамеченной. (Один из способов решения этой проблемы, который сработает не всегда, а при сравнении с константой, заключается в записи: if(0 = *y) ... , что позволит компилятору определить ошибку, но в случае если сравнивать надо со значением другой переменной, этот способ уже не спасет)
Проблема №2 "Смещённый" else, относящийся не к первому if-у, как может показаться, а ко второму. Компилятором не отслеживается. Придётся "оборачивать" все конструкции фигурными скобками...
Проблема №3 Аналогична второй, увеличение y не относится к ветви else, а выполняется безусловно. Опять оборачиваем блок операторов фигурными скобками...
Проблема №4 Неправильный приоритет операций: выражение *y++ будет выполняться как *(y++), а не (*y)++, как Вам хотелось бы. Придётся указывать порядок выполнения скобками, или отделять операцию инкремента пробелом: *y ++.
Итоговый вариант:
include <limits.h> void check_divide (int *k, int x, int *y) { if (*y == 0) { if ( x > 0) *k = INT_MAX; } else { *k = x / *y; (*y)++; /* или *y ++ */ } }
Для сравнения, та же программа на Аде:
procedure Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is begin if Y = 0 then if X > 0 then K := Integer’Last; -- K приравниваем наибольшему значению для целого end if; else K := X / Y; -- Деление на Y безопасно, он не может быть равен 0 Y := Y + 1; end if; end Check_Divide;
, или даже:
procedure Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is begin if Y = 0 and then X > 0 then K := Integer’Last; elsif Y /= 0 then K := X / Y; Y := Y + 1; end if; end Check_Divide;
То есть: 1) невозможно "перемешать" оператор присваивания (:=) и оператор сравнения (=), поскольку выражение становится некорректным синтаксически, компилятор его не пропускает; 2) проблемы "смещённого" else в Аде не существует, каждый оператор if обязан закончиться end if, иначе будет ошибка времени компиляции; 3) то же самое касается и "безусловно" выполняемого выражения - все то, что предшествует end if, относится к условию; 4) поскольку операции разыменования в языке нет, то проблема снимается сама собой.
P.S. С++ чуть более безопасен в этом плане, если передать y по ссылке, то одной проблемой будет меньше
3. Что касается ассоциативности и приоритета операций:
Что означает условие
if (x & mask == 0) ...
?
Оно компилируется, но...(Показать/Скрыть)
не означает if ((x & mask) == 0), а означает if (x & (mask == 0)), компилятор не в состоянии помочь программисту найти ошибку из-за неоднозначности
А вот это:
if (x < y < z) ...
?
И это тоже компилируется, но...(Показать/Скрыть)
не означает if ((x < y) && (y < z)), а означает if (((x < y) && (1 < z)) || (0 < z)) И здесь компилятор бессилен...
Налицо не совсем ожидаемое поведение.
Что касается Ады (и других Паскаль-подобных языков):
if x and mask = 0 … -- выдаст ошибку при компиляции
Требуется расставить скобки, чтобы это откомпилировать. А значит, опечатка программиста или просто невнимательность - исключены. Больше того, поскольку подобная операция со знаковыми числами может быть небезопасной, Ада запрещает производить ее с теми операндами, которые не являются переменными типа Unsigned_xx
if x < y < z ... -- Аналогично: ошибка компиляции
Требуется уточнение условия: if (x < y) and (y < z) …
4. Еще о синтаксисе...
Корректен ли следующий код:
// -- Если впереди "зелёный" свет - увеличить скорость void increase_speed_if_safe (int speed, int signal) { if (signal == CLEAR); increase_speed (); }
?
Код компилируется без предупреждений, но...(Показать/Скрыть)
Лишняя точка с запятой, в результате, независимо от значения signal, скорость будет увеличена. К чему это может привести ;) ?
Для сравнения:
-- Если впереди "зелёный" свет - увеличить скорость procedure increase_speed_if_safe (speed : integer; signal : integer) is begin if signal = CLEAR then; -- Ошибка времени компиляции increase_speed; end if; end increase_speed_if_safe;
5. Поговорим о перечислениях...
enum Alert_Type {LOW, MEDIUM, HIGH, VERY_HIGH};
void handle_alert (enum Alert_Type alert) { switch (alert) { case LOW: activate_camera (); case MEDIUM: send_guard (); case HIGH: sound_alarm (); } }
void process_alerts () { handle_alert (2); ...
Программа компилируется, но выполняться будет совсем не так, как кажется. В чем дело?
А дело в том, что...(Показать/Скрыть)
1) после окончания каждой ветки case требуется добавление break, чтобы выйти из оператора switch, иначе будут выполняться коды всех меток, расположенных ниже, до первого break-а или до окончания оператора switch...
Кстати, по поводу того, что для завершения текущей ветки нужны break-и: несколько лет назад компания Sun Microsystems провела исследования Сишных программ на предмет использования оператора switch. Выяснилось, что в 95% случаев после каждой ветки стоят break-и. То есть, "проваливание" использует только очень малый процент программистов. Подавляющее большинство вынуждено вносить в свою программу дополнительные операторы, чтобы получить более часто используемый функционал.
2) и С и С++ (да и Java тоже) успешно компилируют программу, несмотря на то, что остались неохваченные оператором switch возможности (в лучшем случае выдается предупреждение). В приведенном выше случае, что будет, если мы передадим в функцию значение VERY_HIGH? 3) в С элементы перечисления являются обычными целочисленными значениями, и даже вызов handle_alert(6) успешно пройдет компиляцию, несмотря на то, что в перечислении столько значений просто нет...
Правильный вариант:
void handle_alert (enum Alert_Type alert) { switch (alert) { case LOW: activate_camera (); break; case MEDIUM: send_guard (); break; case HIGH: sound_alarm (); break; case VERY_HIGH: alert_police (); break; } } void process_alerts () { handle_alert (HIGH); ...
Для сравнения:
type Alert_Type is (LOW, MEDIUM, HIGH, VERY_HIGH); procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when HIGH => Sound_Alarm; when VERY_HIGH => Alert_Police; end case; end Process_Alert;
1) при вызове процедуры компилятором будут пропущены только значения типа Alert_Type; 2) нет необходимости в аналоге break для выхода из оператора case (в любом случае по окончании выполнения кода соответствующего when-селектора выполнение case будет завершено); 3) в случае, если будет упущен один из вариантов перечисления, компилятор выдаст ошибку; 4) гибкие селекторы - можно сделать и так:
procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when HIGH | VERY_HIGH => -- или HIGH или VERY_HIGH Sound_Alarm; Alert_Police; end case; end Process_Alert;
и так:
procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM .. VERY_HIGH => -- значения MEDIUM, HIGH, VERY_HIGH Send_Guard; Sound_Alarm; Alert_Police; end case; end Process_Alert;
, и вот так:
procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when others => -- все остальные случаи Sound_Alarm; Alert_Police; end case; end Process_Alert;
6. Неопределенности
Что здесь происходит:
{ int k = 0; int v [10]; k = v [k++]; }
?
А происходит здесь...(Показать/Скрыть)
неопределённое поведение. См. Стандарт языка С++ по поводу точек следования. Между двумя точками следования переменная не может изменить значение больше одного раза, а в вышеприведенном примере значение меняется дважды. Причем, компилятор на подобные вещи вообще не реагирует. Никак.
В Аде это запрещено синтаксисом.
7. Проблемы с системой типов языка.
Программа компилируется, но все равно в ней что-то не так:
typedef int Time; typedef int Distance; typedef int Speed;
// ... const Speed SAFETY SPEED = 120;
void increase_speed (Speed s);
// … void check_speed (Time t, Distance d) { Speed s = d/t; if (s < SAFETY_SPEED) increase_speed (t); }
void perform_safety_checks () { Time t = get_time (); Distance d = get_distance (); // … check_speed (d, t); }
Что именно?
Уууу... Да здесь целый букет...(Показать/Скрыть)
Проблема №1
if (s < SAFETY_SPEED) increase_speed (t); // <--- t - это время, а не скорость
Проблема №2
check_speed (d, t); // <--- обратите внимание на порядок параметров
С/С++ не может в данном случае помочь программисту понять, что совершена ошибка.
Понятно, что на Аде можно написать точно так же:
-- Некорректный код, только для примера !!! SAFETY_SPEED : constant Integer := 120; -- … procedure Increase_Speed (S: Integer);
procedure Check_Speed (T : Integer; D : Integer) is S : Integer := D / T; begin if S < SAFETY_SPEED then Increase_Speed (T); end if; end Check Speed;
procedure Perform_Safety_Checks is T : Integer := Get_Time; D : Integer := Get_Distance; begin -- … Check_Speed (D, T); end Perform Safety Checks;
Но у Ада-программ есть еще 2 линии защиты, которые отсутствуют в С/С++ 1) типы, определяемые пользователем; 2) ассоциации параметров.
Итак:
-- определяем 3 новых целочисленных типа type Time is range 0 .. 3_600; type Distance is range 0 .. 1_000; type Speed is range 0 .. 4_000;
SAFETY_SPEED : constant Speed := 120;
procedure Check_Speed(T : Time; D : Distance) is S : Speed := D / T; -- Ошибка компиляции: Это не 3 целых числа, это 3 разнотипных значения begin if S < SAFETY_SPEED then Increase_Speed(T); -- Ошибка компиляции: T не типа Speed !!! end if end Check_Speed;
procedure Perform_Safety_Checks is T : Time := Get_Time; D : Distance := Get_Distance; begin -- … Check_Speed (D, T); -- Ошибка компиляции: несоответствие типов параметров end Perform_Safety_Checks;
Вот так будет выглядеть корректная программа:
type Time is range 0 .. 3_600; type Distance is range 0 .. 1_000; type Speed is range 0 .. 4_000;
SAFETY_SPEED : constant Speed := 120;
procedure Check_Speed(T : Time; D : Distance) is S : Speed := Speed(Integer(D) / Integer(T)); -- явное приведение типов !!! begin if S < SAFETY_SPEED then Increase_Speed(S); -- Ok end if end Check_Speed;
procedure Perform_Safety_Checks is T : Time := Get_Time; D : Distance := Get_Distance; begin -- … Check_Speed (T, D); -- Ok end Perform_Safety_Checks;
8. Проблема переполнения.
Что произойдет в следующем фрагменте программы на С (или на С++):
#include <limits.h> void compute () { int k = INT_MAX; k = k + 1; }
?
...(Показать/Скрыть)
В С/С++ переполнение знакового/беззнакового целого не ловится (несмотря на то, что существует EXCEPTION_INT_OVERFLOW) по крайней мере на нескольких платформах, случиться может все что угодно. Как workaround предлагается нечто подобное: Implemented integer overflow class (Очень удобно, правда? )
В Аде каждый раз, когда происходит переполнение выбрасывается исключение (на всех поддерживаемых платформах). То же смое касается и попытки деления на 0, и выхода за пределы массива, ну, и так далее...
Disclaimer Только не надо говорить, что примеры-искусственные, и специально подобраны так, что Ада показана выигрышно. Попробуйте привести примеры, как избежать в С/С++ тех неоднозначностей, о которых я написал, чтобы дать возможность компилятору не пропустить ошибку или недочет программиста.
По большей части смысл данной темы - в том, чтобы показать, что большинство проблем связано с излишней "гибкостью" языка, в котором "разрешено все то, что не запрещено" (а запрещено явно очень немного вещей), что то же самое и даже гораздо большее можно сделать и без этих правил, разрешающих "всё и вся", с таким строгим синтаксисом, как у Ады.
А вместо диапазона писать название перечислимого типа можно?
Легко... Будет перебираться весь тип, от самого первого до самого последнего значения, и всего делов. Обрати внимание, для того чтобы напечатать идентификатор элемента, достаточно сделать MyType'Image. Это тоже встроено в язык, не нужны никакие Дельфийские шаманства с RTTI.
Цитата
А, контроль вариантных полей во время выполнения (во время компиляции, увы, никак).
Увы - это ты про Дельфи? Вот это что-ли?
(правда тут у тебя небольшой облом - селектор должен задаваться один раз, при инициализации переменной. Менять его ты не имеешь права. Задавать переменную без селектора - тоже). Но как доп. средство для контроля - пойдет.