Почему язык программирования допускает присваивание от целого числа к короткому?

program TypeCategory;
{$R+}
var
    sInt : shortint;
    iInt : integer;
begin
    readln(iInt);
    sInt := iInt;
    writeln(sInt);
end.

Учитывая приведенный выше пример, язык паскаль позволяет присваивать от integer до shortint или даже от longint до shortint без явного приведения типов. То есть паскаль разрешает присваивание внутри категории типов (здесь целые типы).

Паскаль известен своим strongly typed, но почему он допускает такие вещи weakly typed? Я думаю, что такой синтаксис будет поощрять несогласованность кода.

Каковы плюсы такого синтаксиса? Существуют ли другие языки, использующие такой синтаксис, кроме знаменитых C и C++?

благодаря.

EDIT:
Я протестировал только turbo pascal и free pascal with fpc/objfpc/tp/delphi model. Кроме того, gcc/g++ и msvc дают одинаковый результат. То есть, присвоение от int (размер которого составляет 4 байта на моем компьютере) до short int (размер 2) не вызовет никаких ошибок компиляции, в то время как вы можете установить соответствующие параметры, чтобы компиляторы генерировали possible lose of data предупреждений.


person Jichao    schedule 17.01.2010    source источник
comment
ИМО, если вы говорите о Паскале, вы не должны упоминать общие языки программирования в своем заголовке.   -  person Bruno Reis    schedule 17.01.2010
comment
на самом деле C и C++ позволяют то же самое.   -  person Jichao    schedule 17.01.2010
comment
Раньше я работал над проектом Delphi с двумя сотнями таблиц, отображаемых как шлюзы строк, где первичные ключи были 32-битными целыми числами, и ВНЕЗАПНО оказалось, что нам нужны ключи большего размера). Поэтому я только что заменил все целые числа с помощью инструмента замены в файле и включил проверку диапазона, оставив целые числа только в конструкциях for .. to .. count/length (к счастью, редактор delphi поддерживает регулярные выражения для поиска такого рода подсчета). Поэтому я бы рекомендовал с самого начала выбрать целочисленный тип).   -  person Boris Treukhov    schedule 22.01.2010
comment
Такого рода вещи были хуже в COBOL, где вам нужно было сказать, сколько цифр имеет число (и если вы укажете слишком мало где-то в цепочке обработки, они будут усечены). С другой стороны, большинство вещей было хуже в COBOL.   -  person David Thornley    schedule 22.01.2010
comment
[мое предположение] Pascal делает это, потому что C делает это. C делает это для удобства, простоты переносимости между архитектурами, которые могут не поддерживать различные размеры целых чисел. Но, как упоминал выше Борис Т., вы должны пожертвовать этим удобством для распределения детерминированного размера.   -  person Chris O    schedule 23.01.2010
comment
@Chris O: По крайней мере, первоначальная разработка Pascal была в значительной степени свободна от влияния C. В истории программирования для персональных компьютеров был довольно долгий период, когда Pascal был доминирующим языком (первоначальный API MacOS был разработан для вызова из Pascal), прежде чем C взял верх.   -  person David Thornley    schedule 23.01.2010
comment
Основное влияние на Паскаль оказал Алгол 60. Вирт вместе с Тони Хоаром разработал два расширения Алгола, Алгол X и Алгол W, оба в 1966 году. В Википедии Паскаль был разработан в 1968 году.   -  person Charles Stewart    schedule 25.01.2010


Ответы (5)


Часть 1. Об определениях

Прежде всего, какой язык и реализацию вы называете паскалем? Если вы говорите об ISO Pascal чем он умер много лет назад. Если вы говорите о каком-то другом языке или реализации, предоставьте дополнительную информацию.

Во-вторых (как уже упоминал Теун Д.), не существует определения термина "строгая типизация". Взгляните на статью Википедии о строгой типизации.

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

Предположим, что мы следуем определению из статьи Луки Карделли Typeful Programming, описанной ниже на странице Википедии:

В статье Луки Карделли Typeful Programming строгая типизация описывается просто как отсутствие непроверенных ошибок типа во время выполнения. В других текстах отсутствие непроверенных ошибок во время выполнения называется безопасностью или безопасностью типов; В ранних работах Тони Хоара это называется безопасностью собственности.

В любом случае описанное поведение не может быть классифицировано как статичная (или безопасная) дисциплина типизации. Мне лично очень не нравится это хммм... Ну это не фича, это баг. знак равно

Часть 2. Ответ

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

Существуют ли какие-либо другие языки, применяющие такой синтаксис, кроме знаменитых C и C++?

Я думаю, что почти каждый статически типизированный язык с различными типами целых чисел имеет такое поведение. Было хорошей идеей иметь эти SHORTINT и все такое прочее в первые годы для экономии памяти. Но теперь, когда почти каждый ПК имеет около 1 ГБ и более оперативной памяти... Предположим, у нас есть 1 миллион 4-байтовых целых чисел вместо 2-байтовых коротких чисел. Это всего около 4 МБ ОЗУ вместо 2 МБ. Я думаю, что это разумная цена за отсутствие всего этого странного поведения, которое вы описали.

Взгляните на Оберон-07 Вирта (Language Report , PDF). Существует только один целочисленный тип — 32-битное INTEGER.

Также можно упомянуть Python (или, может быть, какой-то другой современный язык с динамической типизацией) с типом int, который представляет числа в неограниченном диапазоне, только в зависимости от доступной (виртуальной) памяти.

Таким образом, вы можете видеть тенденцию - разнообразие типов целых чисел - это выживание 70-х. знак равно

Каковы плюсы такого синтаксиса?

Плюсы (вероятно) уменьшение многословия. Эти статически типизированные языки уже настолько многословны, что если мы решим добавить некоторое явное преобразование целочисленного типа, как это сделал Вирт в Oberon-2 (взгляните на функции SHORT() и LONG()), они станут еще более многословными. В качестве компромисса можно разрешить неявное преобразование. Также во многих языках фактический размер переменных целочисленных типов не фиксирован и отличается от одной реализации к другой. Единственная доступная информация заключается в том, что size(shortint) ‹= size(int). В случае равенства явное преобразование выглядит довольно странно.

Часть 3. Дифирамб Оберону-2 =)

Кстати, не беспокойтесь слишком о Паскале. Он мертв, но в Оберон-2 Никлаус Вирт исправил свою ошибку.

В Главе 6 Language Report вы можете найти информацию о типах. Для нашего обсуждения важным утверждением является:

Типы с 3 по 5 — целочисленные типы, типы 6 и 7 — вещественные типы, и вместе они называются числовыми типами. Они образуют иерархию; больший тип включает (значения) меньшего типа: LONGREAL ›= REAL ›= LONGINT ›= INTEGER ›= SHORTINT

В Главе 9 мы можно прочитать о заданиях:

Выражение должно быть совместимо по назначению с переменной

Наконец, в Приложении А:

Совместимость с заданиями

Выражение e типа Te совместимо по присваиванию с переменной v типа Tv, если выполняется одно из следующих условий:

Te и Tv одного типа;

Te и Tv являются числовыми типами, и Tv включает Te;

...

Итак, мы здесь. Вы не можете присвоить выражение INTEGER переменной SHORTINT. Если вам интересно, вы также можете взглянуть на Component Pascal, второстепенный вариант и усовершенствование Oberon. -2. Создание компонентов BlackBox – это интегрированная среда разработки для Windows.


В ответ на комментарий Джастина Смита.

Я поражен, что он сказал, что больший тип включает (значения) меньшего типа: LONGREAL ›= REAL ›= LONGINT ›= INTEGER ›= SHORTINT, учитывая, что существуют LONGINT, которые не могут быть выражены как REAL.

Я немного смущен вашим утверждением

есть LONGINTS, которые не могут быть выражены как REAL

На самом деле на моей машине IDE, упомянутая выше, имеет

МАКС(ДЛИННОЕ) = 9223372036854775807

МАКС(РЕАЛЬНОЕ) = 1,797693134862316E+308

Таким образом, вы можете представить каждое LONGINT как РЕАЛЬНОЕ число. Но представление может быть неточным. Я думаю, что вы на самом деле говорили об этом, но мы говорим здесь о преобразовании различных целочисленных типов. А преобразование между REAL и INTEGER — это совсем другая история. История плохого и запутанного нейминга. НАСТОЯЩИЕ числа на самом деле не являются настоящими числами с математической точки зрения. Они являются некоторым приблизительным представлением. Можно использовать приближение рациональных чисел (с сохранением числителя и знаменателя в виде целых чисел), но чаще всего использование аппроксимации с плавающей запятой. Стандарт IEEE для вычислений с плавающей запятой (также известный как IEEE 754) является наиболее широко используемый стандарт для вычислений с плавающей запятой.

Каждый должен знать, что НАСТОЯЩИЕ числа — это не настоящие, а числа, описанные в стандарте IEEE 754. И каждый должен прочитать Что должен знать каждый программист об арифметике с плавающей запятой уточнить некоторые моменты.

Но это уже другая история... =)

person Wildcat    schedule 22.01.2010
comment
Я поражен, что он сказал больший тип включает (значения) меньшего типа: LONGREAL ›= REAL ›= LONGINT ›= INTEGER ›= SHORTINT, учитывая, что существуют LONGINT, которые не могут быть выражены как REAL. . - person Justin Smith; 22.01.2010
comment
9223372036854775806.0 == (9223372036854775806.0 + 1) Это потому, что ни 9223372036854775806, ни 9223372036854775807 не могут быть представлены как действительные числа. Вот почему их следует называть с плавающей запятой. - person Justin Smith; 23.01.2010
comment
Я тестировал только turbo pascal, free pascal with fpc/objfpc/delphi model. Но я думаю, что delphi будет таким же. - person Jichao; 23.01.2010
comment
@jcyang: да, и турбо, и бесплатный паскаль позволяют присваивать значения от целого числа к короткому. И я думаю, что такое поведение — это пережиток 70-х. =) Я отредактировал свое сообщение, пытаясь доказать эту мысль. - person Wildcat; 23.01.2010
comment
Смотрите мой пост ниже. Существует также вопрос о том, что представляет собой Тип. Но Паскаль определенно не является строго типизированным, позволяя использовать смешанные целочисленные и действительные (с плавающей запятой) выражения (хотя для этого требуется, чтобы результат был реальным). - person Marco van de Voort; 21.04.2013

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

person Mike Hanson    schedule 22.01.2010

Я думаю, что это поведение не является слабо типизированным в том смысле, что вы никогда не сможете получить указатель на shortint, который действительно указывает на integer. Это также никогда не приведет к сбою (хотя числовое значение во второй переменной может отличаться от ваших ожиданий).

Вы имеете в виду, что, по-видимому, Паскаль не проверяет переполнение. Например, C# вы можете установить поведение проверки из кода ( http://msdn.microsoft.com/en-us/library/khy08726(VS.71).aspx), но часто это делается не из соображений производительности. Я предполагаю, что это относится и к паскалю.

person Teun D    schedule 17.01.2010
comment
java не допускает такого рода вещей. Паскаль проверяет переполнение, если включает директиву компилятора проверки диапазона с помощью {$R+}, но это может повлиять только на константную ошибку компиляции и ошибку времени выполнения. То есть Паскаль обрабатывает это как исключение, а не ошибка компилятора. - person Jichao; 17.01.2010
comment
you can never get a pointer to a shortint that really points to a integer.?Pascal допускает присваивание адреса целого числа короткому указателю. - person Jichao; 23.01.2010
comment
Джичао: Версии, производные от Borland, работают, а классический (и стандартный) Pascal — нет. - person Marco van de Voort; 22.04.2013

Если бы язык никогда не допускал присваивания от int к short int, было бы невозможно написать этот код

var
sInt : shortint;
iInt : integer;

if (iInt < 100 && iInt > -100)
{
   sInt := iInt; // this would not compile
}

или это

var
sInt : shortint;
iInt : integer;

sInt := (iInt & 0xFFFF); // this would not compile

Вы не могли бы написать код, который использует целые числа как временные значения, выполняя математические операции с короткими целыми числами, как этот.

var
sInt1 : shortint;
sInt2 : shortint;
iInt : integer;

/* I want to multiply sInt2 by sInt1 and then divide by 100 */
iInt = sInt2;
sInt2 := (iInt * sInt1) / 100; // this would not compile

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

person John Knoeller    schedule 27.01.2010
comment
Я думаю, его точка зрения заключалась в том, почему язык не заставляет вас делать состав явным? Я не думаю, что он предлагает полностью запретить назначение между типами. - person Chinmay Kanchi; 27.01.2010
comment
если бы приведение победило предупреждения о возможном усечении, это было бы так же плохо или еще хуже. Приведение может быть непреднамеренным усечением так же легко, как и присваивание. - person John Knoeller; 27.01.2010

Насколько я знаю, есть несколько вещей, которые следует учитывать:

  1. Существует только ОДИН целочисленный тип в классическом понимании Паскаля, integer. Остальные представляют собой просто поддиапазоны и, следовательно, не являются полностью отдельным типом. Назначения разрешены в зависимости от диапазона, и проверки диапазона во время выполнения являются частью стандарта для обеспечения его соблюдения.
  2. В общем Паскаль позволяет присваивать числовые типы, если диапазоны совпадают. Вот почему разрешено добавление целого числа к вещественному. (считается, что real содержит диапазон целых чисел), но не наоборот. Однако это особое исключение (преобразование, определяемое языком для простоты использования), поскольку real не является даже порядковым типом.

Классический Паскаль часто определял базовый целочисленный тип, отличный от размера машинного слова. (обычно в два раза больше), к самому большому типу, который можно обработать на машине, и использовать поддиапазоны для всех типов переменных. Это означает, что различные вычисления, как во время выполнения, так и во время компиляции (константы), будут продолжать работать, даже если размер машинного слова время от времени превышается за счет некоторой производительности. Они просто будут увеличены до большего целочисленного типа. Я узнал об этом только из телеконференций, хотя у меня не так много опыта в этом. Я не знаю, было ли автоматически увеличено какое-либо умножение. Вероятно, это менее применимо в настоящее время, когда большая часть обработки не выполняется в пакетном режиме с четко определенными входными данными и штатом инженеров для их обработки.

Установка диапазонов для всех переменных также сделала проверку диапазона более эффективной.

e.g.

var x : 0..10;

x:=10;
x:=x+1; // runtime range check error  here.

Однако это использование поддиапазонов для определения переменных пошло на убыль, поскольку эра DOS привела к появлению более случайного типа программиста. В Turbo Pascal почти все было напечатано как «целое число», и, таким образом, это должно было быть машинное слово (или меньше) из соображений эффективности.

Пришлось ввести longint с удвоенным размером диапазона (а позже int64 в Delphi. Может быть, int128 для x86_64 со временем), но это всегда оставалось несколько своеобразным (и обычные правила ранжирования не применялись. Также проблемы с беззнаковым типом, соответствующим к самому большому подписанному типу никогда не были полностью разрешены (поскольку базовый тип Pascal подписан и, следовательно, не может содержать самый большой беззнаковый тип)

person Marco van de Voort    schedule 21.04.2013