Що таке безпечний тип?


Відповіді:


246

Безпека типу означає, що компілятор перевірятиме типи під час компіляції та видасть помилку, якщо ви спробуєте призначити неправильний тип змінній.

Деякі прості приклади:

// Fails, Trying to put an integer in a string
String one = 1;
// Also fails.
int foo = "bar";

Це стосується також аргументів методу, оскільки ви передаєте їм явні типи:

int AddTwoNumbers(int a, int b)
{
    return a + b;
}

Якщо я спробував зателефонувати, використовуючи:

int Sum = AddTwoNumbers(5, "5");

Компілятор видасть помилку, тому що я передаю рядок ("5"), і він очікує ціле число.

На мові, що не вводиться, як-от JavaScript, я можу зробити наступне:

function AddTwoNumbers(a, b)
{
    return a + b;
}

якщо я називаю це так:

Sum = AddTwoNumbers(5, "5");

Javascript автоматично перетворює 5 в рядок і повертає "55". Це пов’язано з JavaScript за допомогою знака + для об'єднання рядків. Для того, щоб це було відомо про тип, вам потрібно зробити щось на кшталт:

function AddTwoNumbers(a, b)
{
    return Number(a) + Number(b);
}

Або, можливо:

function AddOnlyTwoNumbers(a, b)
{
    if (isNaN(a) || isNaN(b))
        return false;
    return Number(a) + Number(b);
}

якщо я називаю це так:

Sum = AddTwoNumbers(5, " dogs");

Javascript автоматично перетворює 5 в рядок і додає їх, щоб повернути "5 собак".

Не всі динамічні мови настільки ж прощають, як JavaScript (насправді динамічна мова не означає, що мова йде про вільну мову (див. Python)).

Незважаючи на те, що це зручно, він відкриває вам безліч помилок, які можна легко пропустити і виявити лише тестуючи запущену програму. Особисто я вважаю за краще, щоб мій компілятор сказав мені, чи допустив я цю помилку.

Тепер повернемося до C # ...

C # підтримує мовну функцію під назвою коваріація , це в основному означає, що ви можете замінити базовий тип на дочірній тип і не викликати помилок, наприклад:

 public class Foo : Bar
 {
 }

Тут я створив новий клас (Foo), що підкласи Bar. Тепер я можу створити метод:

 void DoSomething(Bar myBar)

І зателефонуйте, використовуючи як Foo, або Bar як аргумент, і те, і інше буде працювати, не викликаючи помилки. Це працює, тому що C # знає, що будь-який дочірній клас Bar буде реалізовувати інтерфейс Bar.

Однак ви не можете зробити зворотне:

void DoSomething(Foo myFoo)

У цій ситуації я не можу передати Bar до цього методу, оскільки компілятор не знає, що Bar реалізує інтерфейс Foo. Це тому, що дочірній клас може (і зазвичай буде) сильно відрізнятися від батьківського класу.

Звичайно, зараз я пішов із глибокого кінця і вийшов за рамки оригінального питання, але його всі хороші речі знати :)


26
Я вважаю, що ця відповідь неправильна: безпека типу не обов'язково застосовується під час компіляції. Я розумію, що, наприклад, схема вважається безпечною для типу, але вона динамічно перевіряється (безпека типу застосовується під час виконання). Це здебільшого перефразовує вступ Бенджаміна К. Пірса до вкладів про типи та мови програмування.
Ніколя Ріноу

11
Те, що ви описуєте, називається поліморфізмом, а не коваріацією. Коваріація використовується в генериці.
IllidanS4 хоче, щоб Моніка повернулася

@NicolasRinaudo зауважимо, що розрив між динамічними мовами та статикою зменшується динамічною компіляцією та попередньою компіляцією для "інтерпретованих" мов та відображенням у "складених" мовах. Рефлексія дозволяє вводити качку під час виконання, наприклад, так що компільована мова може сказати "ей, це метод Quack (), я зателефоную це і побачу, що відбувається". Мови, що нагадують паскаль, також часто мають перевірку переповнення часу (необов'язково), що призводить до того, що помилки "компілятора", що трапляються під час виконання, "не можуть вмістити цілі числа, поставлені в 8-бітове призначення {core dump}".
Кодовий мерзотник

2
Ваш приклад посилається на концепцію під назвою "сильно набрана", яка не є такою ж, як безпека типу. Безпека типу - це коли мова може виявити помилки типу під час виконання або компіляції. Наприклад, Python є слабо типізованим і безпечним. Цю відповідь слід позначити, оскільки вона дуже вводить в оману.
dantebarba

пояснення в цілому проти хорошого, але безпека типу не така, як сильно набрана
senseiwu

57

Безпеку типу не слід змішувати зі статичним / динамічним набором тексту або сильним / слабким набором тексту.

Мова, безпечна для типу, - це лише те, що єдині операції, які можна виконати над даними, - це ті, які підтверджуються типом даних. Тобто, якщо ваші дані мають тип Xі Xне підтримують операцію y, мова не дозволить виконати y(X).

Це визначення не встановлює правила, коли це встановлено. Це може бути під час компіляції (статичне введення тексту) або під час виконання (динамічне введення тексту), як правило, через винятки. Це може бути і те і інше: деякі статично набрані мови дозволяють передавати дані з одного типу в інший, а дійсність кастів потрібно перевіряти під час виконання (уявіть, що ви намагаєтеся передати " Objecta" Consumer- у компілятора немає спосіб дізнатися, прийнятний він чи ні).

Безпека типу не обов'язково означає сильно набраний текст - деякі мови, як відомо, слабо набрані, але все-таки, певно, є безпечними. Візьмемо, наприклад, Javascript: його система типів настільки ж слабка, як і раніше, але все-таки суворо визначена. Це дозволяє автоматично вводити дані (скажімо, рядки в ints), але в межах чітко визначених правил. Наскільки мені відомо, жоден випадок, коли програма Javascript буде вести себе не визначено, і якщо ви досить розумні (я не є), ви повинні мати можливість передбачити, що станеться під час читання коду Javascript.

Прикладом небезпечної для мови мови програмування є C: читання / запис значення масиву поза межами масиву має невизначене поведінку за специфікацією . Неможливо передбачити, що станеться. C - мова, яка має систему типів, але не є безпечною для типу.


1
які ще є приклади небезпечних типів мов? Що ви маєте на увазі під «написанням значення масиву поза межами масиву, має невизначене поведінку за специфікацією. Неможливо передбачити, що буде». Як і Javascript, він повернеться невизначеним, чи не так? Або справді все може статися. Чи можете ви навести приклад цього?
ARK

1
@AkshayrajKВпевнений. Масиви - це покажчики пам’яті, тому, виписуючи межі, ви можете перезаписати дані іншої програми - що нічого не може зробити, збій програми, змушує стерти ваш жорсткий диск - це не визначено і залежить від того, хто читає цей біт пам'яті і як вона на це відреагує.
Ніколя Ріноу

@Nicolas Rinaudo Це невірно. Ви повинні прочитати про віртуальну пам'ять. Кожен процес має власний віртуальний адресний простір, тому процес не може "перезаписати дані іншої програми" таким чином.
ілстам

Ви маєте рацію - це повинно було прочитати, що ви можете перезаписати іншу частину пам’яті вашої програми - аж до, я вважаю, самої програми?
Nicolas Rinaudo

@NicolasRinaudo Кодовий сегмент програми відображається лише для читання у віртуальному адресному просторі. Отже, якщо ви спробували написати це, це призведе до помилки сегментації, і ваша програма вийде з ладу. Так само, якщо ви спробували записати на незроблену пам'ять, що призведе до помилки сторінки і знову збоїв. Однак, якщо вам не пощастило, ви можете просто перезаписати дані з стека процесу чи купи (як інші змінні чи інші речі). У такому випадку ви, ймовірно, не зазнаєте аварії відразу, що ще гірше, тому що ви не помітите помилку до (сподіваємось) пізніше!
ільстам

32

Безпека типу - це не просто обмеження часу компіляції, а обмеження часу виконання . Я відчуваю, що навіть через весь цей час ми можемо додати додаткової ясності цьому.

Є 2 основні питання, пов'язані з безпекою типу. Пам'ять ** та тип даних (з відповідними операціями).

Пам'ять **

char, Як правило , потрібно 1 байт на символ, або 8 біт ( в залежності від мови, Java і C # магазину Юникода символів , які вимагають 16 біт). Потрібно int4 байти або 32 біта (як правило).

Візуально:

char: |-|-|-|-|-|-|-|-|

int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Безпечна мова типу не дозволяє вставляти int в char під час виконання (це повинно викинути вигляд виключення класу або виключення з пам'яті). Однак, в небезпечній мові типу, ви перезаписали б наявні дані в 3 сусідні байти пам'яті.

int >> char:

|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|

У вищенаведеному випадку 3 байти праворуч перезаписуються, тому будь-які вказівники на цю пам’ять (скажімо, 3 послідовних символи), які очікують отримати передбачуване значення char, тепер матимуть сміття. Це спричиняє undefinedповедінку у вашій програмі (або ще гірше, можливо, в інших програмах залежно від того, як ОС розподіляє пам'ять - дуже малоймовірно в ці дні).

** Хоча цей перший випуск технічно не стосується типу даних, введіть безпечні мови вирішуйте його по суті, і він візуально описує проблему тим, хто не знає, як виглядає розподіл пам'яті.

Тип даних

Більш тонка і пряма проблема полягає в тому, коли два типи даних використовують однакове розподілення пам'яті. Візьміть int vs unsigned int. Обидва - 32 біти. (Так само легко можуть бути знаки [4] та int, але більш поширеною проблемою є uint vs. int).

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Небезпечна мова типу дозволяє програмісту посилатись на правильно виділений проміжок у 32 біти, але коли значення неподписаного int зчитується у просторі int (або навпаки), ми знову маємо undefinedповедінку. Уявіть проблеми, які це може спричинити в банківській програмі:

"Чувак! Я завищив $ 30, і тепер у мене залишилося $ 65 506 !!"

..., звичайно, банківські програми використовують значно більші типи даних. ;) ЛОЛ!

Як вже вказували інші, наступний випуск - обчислювальні операції на типи. Це вже було достатньо висвітлено.

Швидкість проти безпеки

Більшість програмістів сьогодні ніколи не потребують таких проблем, якщо вони не використовують щось на зразок C або C ++. Обидві ці мови дозволяють програмістам легко порушувати безпеку типу під час запуску (пряме посилання на пам'ять), незважаючи на максимум зусиль компіляторів, щоб мінімізувати ризик. ЯКЩО це не все погано.

Однією з причин цих мов є настільки обчислювально швидкий факт, що вони не обтяжуються, перевіряючи сумісність типів під час операцій, як, наприклад, Java. Вони припускають, що розробник - це хороша раціональна істота, яка не додаватиме рядок і int разом, і для цього розробник винагороджується швидкістю / ефективністю.


27

Багато відповідей тут пов'язують безпеку типу зі статичним набором та динамічним набором тексту. Динамічно набрана мова (як, наприклад, smalltalk) також може бути безпечною для типу.

Коротка відповідь: мова вважається безпечною для типу, якщо жодна операція не призводить до невизначеної поведінки. Багато хто вважає вимогу конверсій явного типу, необхідних для мови, строго вводити, оскільки автоматичні перетворення іноді можуть призводити до чітко визначеної, але несподіваної / неінтуїтивної поведінки.


1
Зачекайте, ваше визначення типу-безпеки не має жодного слова «тіпа»: D if no operation leads to undefined behavior.
ВасиліНовіков

1
Також я б не погодився з таким визначенням. Я думаю, що безпека типу означає саме 1. існування типів 2. знання їх компілятору та відповідні перевірки звичайно.
ВасиліНовіков

10

Мова програмування, який є "безпечним для типу", означає:

  1. Ви не можете читати з неініціалізованих змінних
  2. Не можна індексувати масиви за їх межами
  3. Ви не можете виконувати трансляції типів без перевірки

8

Пояснення спеціаліста з ліберальних мистецтв, а не спеціалістів з спеціальностей:

Коли люди кажуть, що мова чи мовна функція є безпечними для типу, вони означають, що мова допоможе вам запобігти, наприклад, передавати щось, що не є цілим числом, до певної логіки, яка очікує ціле число.

Наприклад, у C # я визначаю функцію як:

 void foo(int arg)

Потім компілятор зупинить мене від цього:

  // call foo
  foo("hello world")

В інших мовах компілятор не зупинить мене (або немає компілятора ...), тому рядок буде переданий логіці і тоді, мабуть, трапиться щось погане.

Введіть безпечні мови, намагайтеся зловити більше за "час компіляції".

З нижньої сторони, із типом безпечних мов, коли у вас є рядок типу "123" і ви хочете оперувати нею як int, ви повинні написати більше коду для перетворення рядка в int або коли у вас є int як 123, і хочете використовувати його в повідомленні типу "Відповідь 123", вам потрібно написати більше коду для перетворення / передачі його в рядок.


4
Ліберальні мистецтва майор сказав би в пояснення :) Ви також прирівнюючи статичної типізації та динамічний введення.
ідідак

1
Ліберальні мистецтва "мажор", а не "мажор".
Corey Trager

5

Для кращого розуміння дивіться відео нижче, яке демонструє код на безпечній мові (C #), а НЕ вводить безпечну мову (javascript).

http://www.youtube.com/watch?v=Rlw_njQhkxw

Тепер довгий текст.

Тип безпеки означає запобігання помилок типу. Помилка типу виникає, коли тип даних одного типу присвоюється іншому типу НЕЗАКОННО, і ми отримуємо небажані результати.

Наприклад, JavaScript не є безпечною мовою типу. У наведеному нижче коді “num” - числова змінна, а “str” - рядок. Javascript дозволяє мені робити «num + str», тепер GUESS буде робити це арифметикою чи конкатенацією.

Тепер для наведеного нижче коду результати "55", але важливим моментом є створення плутанини, яку операцію він буде робити.

Це відбувається тому, що javascript не є безпечною мовою. Це дозволяє встановлювати один тип даних на інший тип без обмежень.

<script>
var num = 5; // numeric
var str = "5"; // string
var z = num + str; // arthimetic or concat ????
alert(z); // displays  “55”
</script>

C # - безпечна мова типу. Це не дозволяє призначити один тип даних іншому типу даних. Наведений нижче код не дозволяє оператору "+" для різних типів даних.

введіть тут опис зображення


4

Безпечний тип означає, що програмно тип даних для змінної, повернутого значення або аргументу повинен відповідати певним критеріям.

На практиці це означає, що 7 (цілий тип) відрізняється від "7" (цитований символ рядкового типу).

PHP, Javascript та інші мови динамічного сценарію зазвичай слабо набрані, тому що вони спробують перетворити (рядок) "7" в (ціле число) 7, якщо ви спробуєте додати "7" + 3, хоча іноді доводиться це робити явно (а Javascript використовує символ "+" для конкатенації).

C / C ++ / Java цього не зрозуміє, або замість результату замінить "73". Безпека типу запобігає таким типам помилок у коді, роблячи явну вимогу типу.

Безпека типу дуже корисна. Рішенням вищезгаданих "7" + 3 було б введення cast (int) "7" + 3 (дорівнює 10).


3

Концепція:

Щоб бути дуже простим Type Safe, як значення, він гарантує, що тип змінної повинен бути безпечним

  1. немає неправильного типу даних, наприклад, не можна зберегти чи ініціалізувати змінну типу рядка з цілим числом
  2. Ізольовані індекси не доступні
  3. Дозволити лише певне місце пам'яті

тому мова йде про безпеку типів вашого сховища з точки зору змінних.


2

Спробуйте це пояснення на ...

TypeSafe означає, що змінні під час компіляції статично перевіряються на відповідне призначення. Наприклад, скопіюйте рядок або ціле число. Ці два типи даних не можуть бути присвоєні між собою (тобто ви не можете призначити ціле число рядку, а також не можете призначити рядок цілому числу).

Для поведінки, що не стосується типу, врахуйте це:

object x = 89;
int y;

якщо ви намагаєтесь це зробити:

y = x;

компілятор видає помилку, яка говорить, що він не може перетворити System.Object в цілий число. Це потрібно робити явно. Один із способів:

y = Convert.ToInt32( x );

Наведене вище завдання не є безпечним. Типове безпечне призначення - це типи, які можуть бути безпосередньо призначені один одному.

Колекції, що не містять типів, містять багато ASP.NET (наприклад, колекції програм, сеансів та перегляду держав). Хороша новина про ці колекції полягає в тому, що (зведення до мінімуму декількох міркувань щодо управління станом сервера) ви можете розмістити майже будь-який тип даних у будь-якій з трьох колекцій. Погана новина: оскільки ці колекції не є безпечними для друку, вам потрібно буде належним чином передати ці значення, коли ви їх отримаєте назад.

Наприклад:

Session[ "x" ] = 34;

працює чудово. Але щоб призначити ціле значення назад, вам потрібно буде:

int i = Convert.ToInt32( Session[ "x" ] );

Прочитайте інформацію про дженерики щодо способів, які допоможуть вам легко реалізовувати колекції typesafe.

C # - безпечна мова, але слідкуйте за статтями про C # 4.0; цікаві динамічні можливості ткацького верстата (чи добре, що C # по суті отримує Option Strict: Off ... ми побачимо).


Особисто я ненавиджу Convert.Зазначенням, чому ви просто не використовуєте безпечний склад? Його єдиний менш функціональний дзвінок також на стакані виклику.
FlySwat

2

Type-Safe - код, який отримує доступ лише до пам'яті, до якої він має право доступу, і лише чітко визначеними, допустимими способами. Код, захищений типом, не може виконувати операцію над об'єктом, недійсним для цього об'єкта. Компілятори мови C # і VB.NET завжди створюють безпечний для типу код, який підтверджується безпечним для типу під час компіляції JIT.


Ви маєте на увазі безпеку пам’яті?
голопот

1

Безпечний тип означає, що набір значень, які можуть бути присвоєні програмній змінній, повинен відповідати чітко визначеним і перевіреним критеріям. Типові безпечні змінні призводять до більш надійних програм, оскільки алгоритми, що управляють змінними, можуть довіряти, що змінна прийме лише одне з чітко визначеного набору значень. Дотримання цієї довіри забезпечує цілісність та якість даних та програми.

Для багатьох змінних набір значень, які можуть бути присвоєні змінній, визначається під час написання програми. Наприклад, змінній під назвою "колір" може бути дозволено приймати значення "червоний", "зелений" або "синій" і ніколи будь-які інші значення. Для інших змінних ці критерії можуть змінюватися під час виконання. Наприклад, змінній під назвою "колір" може бути дозволено приймати значення лише у стовпці "ім'я" таблиці "Кольори" у реляційній базі даних, де "червоний," зелений "та" синій "- це три значення для "name" в таблиці "Colors", але деякі інші частини комп'ютерної програми можуть бути в змозі додати до цього списку під час роботи програми, а змінна може приймати нові значення після їх додавання до таблиці Colors. .

Багато мов, що захищають тип, створюють ілюзію "безпека типу", наполягаючи на чіткому визначенні типів змінних і лише дозволяючи присвоювати змінній значення одного і того ж "типу". Є кілька проблем з таким підходом. Наприклад, програма може мати змінну "yearOfBirth" - це рік народження людини, і це спокушає вводити це як коротке ціле число. Однак це не коротке ціле число. Цього року це кількість, менша за 2009 рік та більша за -10000. Однак цей набір зростає на 1 щороку, коли програма працює. Зробити це "коротким int" не є адекватним. Для забезпечення безпеки цієї змінної типу потрібна функція перевірки часу виконання, яка гарантує, що кількість завжди перевищує -10000 і менше, ніж наступного календарного року.

Мови, які використовують динамічне введення (або набирання качок, або маніфестне введення), такі як Perl, Python, Ruby, SQLite та Lua, не мають поняття введених змінних. Це змушує програміста написати процедуру перевірки часу виконання для кожної змінної, щоб переконатися, що вона правильна, або перенести наслідки незрозумілих винятків під час виконання. З мого досвіду, програмісти на мовах статичного типу, такі як C, C ++, Java та C #, часто переймаються думкою, що статично визначені типи - це все, що потрібно зробити, щоб отримати переваги безпеки типу. Для багатьох корисних комп'ютерних програм це просто не відповідає дійсності, і важко передбачити, чи це правда для будь-якої конкретної комп'ютерної програми.

Довгий і короткий .... Ви хочете захистити тип? Якщо так, то запишіть функції часу виконання, щоб переконатися, що коли змінній присвоєно значення, вона відповідає чітко визначеним критеріям. Недоліком є ​​те, що це робить аналіз домену справді важким для більшості комп'ютерних програм, оскільки вам потрібно чітко визначити критерії для кожної змінної програми.


2
Змінні Python набираються ( фактично сильно набрані). Спробуйте зробити це, наприклад: "str" ​​+ 1. Ви отримаєте помилку. Однак типи перевіряються під час виконання, а не під час компіляції.
mipadi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.