Перейти к

редактировать

Перейти к (Перейти к, Перейти к, Перейти к или другие комбинации регистров, в зависимости от языка программирования) - это оператор, встречающийся во многих компьютерных языках программирования. Он выполняет одностороннюю передачу управления другой строке кода; Напротив, вызов функции обычно возвращает управление. Переходные местоположения обычно идентифицируются с помощью меток, хотя в некоторых языках используются номера строк. На уровне машинного кода gotoпредставляет собой форму инструкции перехода или перехода, в некоторых случаях объединенной с настройкой стека. Многие языки поддерживают оператор goto, а многие нет (см. § языковая поддержка).

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

В прошлом в научных кругах и промышленности велись серьезные споры о достоинствах использования операторов goto. Раньше использование goto было обычным явлением, но с момента появления структурного программирования в 1960-х и 1970-х годах его использование значительно сократилось. Основная критика заключается в том, что код, использующий операторы goto, труднее понять, чем альтернативные конструкции. Goto остается в использовании в некоторых стандартных шаблонах использования, но обычно используются альтернативы, если они доступны. Споры о его (более ограниченном) использовании продолжаются в академических кругах и кругах индустрии программного обеспечения.

Содержание
  • 1 Использование
  • 2 Критика
  • 3 Общие шаблоны использования
  • 4 Альтернативы
    • 4.1 Структурированное программирование
    • 4.2 Исключения
    • 4.3 Хвостовые вызовы
    • 4.4 Сопрограммы
    • 4.5 Продолжение
    • 4.6 Передача сообщений
  • 5 Варианты
    • 5.1 Вычисленный GOTO и назначенный GOTO
    • 5.2 ALTER
    • 5.3 Perl GOTO
    • 5.4 Эмулированный GOTO
    • 5.5 Переменные меток PL / I
    • 5.6 MS / DOS GOTO
  • 6 Поддержка языков
  • 7 См. Также
  • 8 Ссылки
Использование

goto label

Оператор gotoчасто в сочетании с оператором if, чтобы вызвать условную передачу управления.

IFусловие ЗАТЕМgoto метка

Языки программирования налагают различные ограничения на назначение оператора goto. Например, язык программирования C не разрешает переход к метке, содержащейся в другой функции, однако переходы в пределах одной цепочки вызовов возможны с использованием функций setjmp / longjmp.

Критика

На собрании, предшествовавшем ALGOL, состоявшемся в 1959 году Хайнц Земанек открыто поставил под сомнение необходимость утверждений GOTO; в то время никто не обратил внимания на его замечание, в том числе Эдсгер В. Дейкстра, который впоследствии стал культовым противником GOTO. В 1970-х и 1980-х годах произошло сокращение использования операторов GOTO в пользу «структурного программирования » парадигмы, при этом goto критиковали за то, что приводили к «не поддерживаемому спагетти-коду "(см. ниже). Некоторые стандарты кодирования стиля программирования, например стандарты кодирования GNU Pascal, не рекомендуют использовать операторы GOTO. Доказательство Бема – Якопини (1966) не разрешило вопрос о том, следует ли применять структурированное программирование для разработки программного обеспечения, отчасти потому, что конструкция с большей вероятностью скрывала программу, чем улучшала ее, потому что ее применение требовало введения дополнительных локальных переменных. Тем не менее, это вызвало бурные дебаты среди компьютерных ученых, преподавателей, разработчиков языков и разработчиков приложений, которые наблюдали медленный, но неуклонный отход от ранее повсеместного использования GOTO. Вероятно, самая известная критика GOTO - это письмо Эдсгера Дейкстры 1968 года под названием Go To Statement Считается вредным. В этом письме Дейкстра утверждал, что неограниченные операторы GOTO следует отменить в языках более высокого уровня, поскольку они усложняют задачу анализа и проверки правильности программ (особенно тех, которые включают циклы). Само письмо вызвало дебаты, в том числе письмо «GOTO Считается вредным» Считается вредным », отправленное в Коммуникацию ACM (CACM) в марте 1987 года, а также дальнейшие ответы других людей, в том числе Дейкстры. a Несколько разочаровывающая переписка.

Альтернативная точка зрения представлена ​​в Дональде Кнута «Структурированное программирование с помощью инструкций перехода», в котором анализируется множество общих задач программирования и обнаруживается, что в некоторых из них GOTO является оптимальная языковая конструкция для использования. В The C Programming Language, Брайан Керниган и Деннис Ричи предупреждают, что goto"бесконечно злоупотребляем", но также предполагают, что он может использоваться для обработчиков ошибок при завершении функции и для многоуровневых прерываний из циклов. Эти два паттерна можно найти в многочисленных последующих книгах по C других авторов; во вводном учебнике 2007 г. отмечается, что шаблон обработки ошибок - это способ обойти «отсутствие встроенной обработки исключений в языке C». Другие программисты, включая Linux разработчик ядра и программист Линус Торвальдс или инженер-программист и автор книги Стив МакКоннелл, также возражают против точки зрения Дейкстры, заявляя, что GOTO могут быть полезной функцией языка, улучшающей скорость, размер и ясность кода программы, но только при разумном использовании сравнительно разумным программистом. По словам профессора информатики Джона Регера, в 2013 году в коде ядра Linux было около 100 000 экземпляров goto.

Другие ученые заняли более радикальную точку зрения и утверждали, что даже такие инструкции, как breakи returnиз середины циклов - плохая практика, поскольку они не нужны в результате Бема – Якопини, и, таким образом, отстаивали, что циклы должны иметь единственную точку выхода. Например, Бертран Мейер писал в своем учебнике 2009 года, что инструкции типа breakи continue«являются всего лишь старым gotoв овечьей шкуре». Слегка измененная форма результата Бема – Якопини позволяет, однако, избегать дополнительных переменных в структурном программировании, если разрешены многоуровневые разрывы циклов. Поскольку некоторые языки, такие как C, не допускают многоуровневых разрывов через ключевое слово break, некоторые учебники советуют программисту использовать gotoв таких обстоятельствах. Стандарт MISRA C 2004 запрещает goto, continue, а также несколько операторов returnи break. Версия стандарта MISRA C 2012 г. понизила статус запрета на gotoс «обязательного» на «рекомендательный»; в редакции 2012 года есть дополнительное обязательное правило, которое запрещает только обратные, но не прямые переходы с goto.

FORTRAN, введенными конструкциями структурированного программирования в 1978 году, а в последующих версиях - относительно свободными семантическими правилами, регулирующими допустимое использование of goto подтянулись; «расширенный диапазон», в котором программист мог использовать GOTO для входа и выхода из все еще выполняющегося цикла DO, был удален из языка в 1978 году, а к 1995 году несколько форм Fortran GOTO, включая Computed GOTO и Assigned GOTO, имели был удален. В некоторых широко используемых современных языках программирования, таких как Java и Python, отсутствует оператор GOTO - см. языковая поддержка - хотя большинство из них предоставляют некоторые средства для выхода из выделенного, либо выход из, либо перемещение на следующий шаг итерации. Точку зрения, что нарушение потока управления в коде нежелательно, можно увидеть при разработке некоторых языков программирования, например, Ada визуально подчеркивает определения меток с помощью угловых скобок.

Запись 17.10 в comp.lang.c Список часто задаваемых вопросов напрямую касается вопроса использования GOTO, утверждая, что

стиль программирования, как и стиль письма, является своего рода искусством и не может быть кодифицирован жесткими правилами, хотя дискуссии о стиле часто, кажется, сосредоточены исключительно вокруг таких правил. В случае оператора goto уже давно замечено, что неограниченное использование goto быстро приводит к невозможности поддержки кода спагетти. Однако простой, бездумный запрет на оператор goto не обязательно немедленно приводит к красивому программированию: неструктурированный программист так же способен построить византийский клубок без использования каких-либо goto (возможно, вместо этого заменив странно вложенные циклы и логические управляющие переменные). Многие программисты занимают умеренную позицию: обычно следует избегать goto, но при необходимости они приемлемы в нескольких хорошо ограниченных ситуациях: как многоуровневые операторы break, объединить общие действия внутри оператора switch или централизовать задачи очистки в функция с несколькими ошибками возвращает. (...) Слепое избегание определенных конструкций или следование правилам без их понимания может привести к такому же количеству проблем, сколько правила должны были предотвратить. Более того, многие мнения о стиле программирования - это просто мнения. Они могут быть решительно аргументированы и сильно прочувствованы, они могут быть подкреплены кажущимися убедительными доказательствами и аргументами, но противоположные мнения могут столь же сильно ощущаться, поддерживаться и аргументироваться. Обычно бесполезно втягиваться в «войну стилей», потому что по определенным вопросам оппоненты никогда не могут прийти к согласию, или согласиться не согласиться, или перестать спорить.

Общие шаблоны использования

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

Ситуации, в которых часто используется goto, включают:

  • Чтобы сделать код более читаемым и более легким в использовании
  • Чтобы создавать программы меньшего размера и избавиться от дублирования кода
  • Реализуйте конечный автомат , используя таблицу переходов состояний и goto для переключения между состояниями (при отсутствии исключения хвостового вызова ), особенно в автоматически сгенерированных Код C. Например, goto в каноническом синтаксическом анализаторе LR.
  • Реализация многоуровневого прерывания и продолжения, если не поддерживается напрямую в языке; это обычная идиома в C. Хотя Java резервирует ключевое слово goto, на самом деле оно не реализует его. Вместо этого в Java реализованы операторы с метками break и continue. Согласно документации Java, использование goto для многоуровневых разрывов было наиболее распространенным (90%) использованием gotos в C. Java не был первым языком, который использовал этот подход - запрещающий goto, но предоставляющий многоуровневые разрывы - в этом отношении ему предшествовал язык программирования BLISS (точнее, его версия BLISS-11).
  • Суррогаты для одноуровневых операторов break или continue (retry), когда потенциальное введение дополнительных петли могут неправильно влиять на поток управления. Эта практика наблюдалась в Netbsd code.
  • Обработка ошибок (при отсутствии исключений), в частности код очистки, такой как освобождение ресурсов. C ++ предлагает альтернативу оператору goto для этого варианта использования, а именно: Получение ресурсов - это инициализация (RAII) с помощью деструкторов или использования исключений try and catch, используемых в Обработка исключений. setjmp и longjmp - еще одна альтернатива, имеющая преимущество, заключающееся в возможности раскручивать часть стека вызовов .
  • выталкивать стек, например, в Algol, PL / I.

Эти варианты использования относительно часто встречается в C, но гораздо реже в C ++ или других языках с функциями более высокого уровня. Однако в некоторых языках генерация и перехват исключения внутри функции может быть чрезвычайно неэффективной; Ярким примером является Objective-C, где goto - гораздо более быстрая альтернатива.

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

Альтернативы

Структурированное программирование

Современное понятие подпрограммы было изобретено Дэвидом Уилером при программировании EDSAC. Чтобы реализовать вызов и возврат на машине без стека, он использовал особый шаблон самомодифицирующегося кода, известный как прыжок Уиллера. Это привело к способности структурировать программы, используя хорошо вложенные исполнения подпрограмм, взятых из библиотеки. Это было бы невозможно, используя только goto, поскольку целевой код, взятый из библиотеки, не знал бы, куда вернуться.

Позже языки высокого уровня, такие как Pascal, были разработаны для поддержки структурного программирования, которое было обобщено из подпрограмм (также известных как процедуры или функции) к дальнейшим управляющим структурам, например:

Эти новые языковые механизмы заменили эквивалентные потоки, которые ранее были написаны с использованием gotos и ifс. Многостороннее ветвление заменяет «вычисляемый переход», в котором инструкция для перехода определяется динамически (условно).

Исключения

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

Обычно были приняты два решения: способ преждевременного выхода из структурированного блока и в более общем плане исключения - в обоих случаях они поднимаются по структуре, возвращая управление охватывающим блокам или функциям, но не переходите к произвольным участкам кода. Это аналогично использованию оператора return в нетерминальной позиции - не строго структурировано из-за раннего выхода, но мягко ослабляет ограничения структурного программирования. В C, break и continueпозволяют завершить цикл или перейти к следующей итерации без требуется дополнительный оператор whileили if. На некоторых языках также возможны многоуровневые перерывы. Для обработки исключительных ситуаций были добавлены специальные конструкции обработки исключений, такие как try/ catch/ finallyв Java.

Механизмы обработки исключений throw-catch также могут быть легко использованы для создания непрозрачных структур управления, как и goto.

Хвостовые вызовы

В статье представленный на конференции ACM в Сиэтле в 1977 г., Гай Л. Стил резюмировал дискуссию о GOTO и структурированном программировании и заметил, что вызовы процедур в хвостовой позиции процедуры могут наиболее оптимально рассматриваться как прямые передача управления вызываемой процедуре, что обычно устраняет ненужные операции манипулирования стеком. Поскольку такие "хвостовые вызовы" очень распространены в Lisp, языке, где вызовы процедур являются повсеместными, эта форма оптимизации значительно снижает стоимость вызова процедуры по сравнению с GOTO, используемым в других языках. Стил утверждал, что плохо реализованные вызовы процедур привели к искусственному восприятию того, что GOTO дешевле, чем вызов процедуры. Стил далее утверждал, что «в общем случае вызовы процедур можно с пользой рассматривать как операторы GOTO, которые также передают параметры и могут быть единообразно закодированы как машинный код инструкции JUMP», при этом команды манипулирования стеком машинного кода «считаются оптимизация (а не наоборот!) ». Стил привел доказательства того, что хорошо оптимизированные числовые алгоритмы в Лиспе могут выполняться быстрее, чем код, созданный доступными в то время коммерческими компиляторами Фортрана, потому что стоимость вызова процедуры в Лиспе была намного ниже. В Scheme, диалекте Лиспа, разработанном Стилом с Джеральдом Джей Сассманом, оптимизация хвостовых вызовов является обязательной.

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

Сопрограммы

Сопрограммы - это более радикальное ослабление структурированного программирования, позволяющее не только несколько точек выхода (как при возврате в позиции без хвоста), но также несколько точек входа, аналогично операторам goto.. Сопрограммы более ограничены, чем goto, поскольку они могут возобновлять выполняющуюся в данный момент сопрограмму только в определенных точках - продолжая после yield - вместо того, чтобы переходить к произвольной точке в коде. Ограниченная форма сопрограмм - это генераторы, которых достаточно для некоторых целей. Еще более ограниченными являются замыкания - подпрограммы, которые поддерживают состояние (через статические переменные ), но не позицию выполнения. Комбинация переменных состояния и структурированного управления, особенно общего оператора switch, может позволить подпрограмме возобновить выполнение в произвольной точке при последующих вызовах и является структурированной альтернативой операторам goto при отсутствии сопрограмм; это распространенная идиома, например, в Си.

Продолжение

A продолжение похоже на GOTO в том, что он передает управление из произвольной точки программы в ранее отмеченную точку. Продолжение более гибкое, чем GOTO на тех языках, которые его поддерживают, потому что оно может передавать управление из текущей функции, чего GOTO не может сделать в большинстве языков структурированного программирования. В тех реализациях языка, которые поддерживают кадры стека для хранения локальных переменных и аргументов функций, выполнение продолжения включает в себя настройку стека вызовов программы в дополнение к переходу. Функция longjmp языка программирования C является примером escape-продолжения, которое может использоваться для выхода из текущего контекста в окружающий. Оператор GO Common Lisp также имеет это свойство раскрутки стека, несмотря на то, что конструкция имеет лексическую область видимости, поскольку на метку, на которую нужно перейти, можно ссылаться из замыкания.

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

Передача сообщений

В непроцедурных парадигмах goto менее актуален или полностью отсутствует. Одна из основных альтернатив - передача сообщений, которая имеет особое значение в параллельных вычислениях, межпроцессном взаимодействии и объектно-ориентированном программировании. В этих случаях отдельные компоненты не имеют произвольной передачи управления, но общее управление может быть запланировано сложным образом, например, с помощью вытеснения. Влиятельные языки Simula и Smalltalk были одними из первых, кто ввел концепции сообщений и объектов. Благодаря инкапсуляции данных состояния, объектно-ориентированное программирование снизило сложность программного обеспечения до взаимодействий (сообщений) между объектами.

Варианты

В классе операторов goto существует ряд различных языковых конструкций.

Вычисленный GOTO и назначенный GOTO

В Fortran вычисленный GOTOпереходит к одной из нескольких меток в списке, основанный на значении выражения. Пример: goto (20,30,40) i. Эквивалентной конструкцией в C является оператор переключения , а в более новом Fortran рекомендуется использовать оператор CASEсинтаксической альтернативой. BASIC имеет ON... GOTOконструкция, которая достигает той же цели.

В версиях до Fortran 95 Fortran также имел назначенный вариант goto, который передает управление метке оператора (номер строки), которая является хранится (присваивается) целочисленной переменной. К сожалению, переход к целочисленной переменной, которой не было присвоено значение, был возможен и являлся основным источником ошибок, связанных с назначенными gotos. Оператор Fortran assignпозволяет присвоить целочисленной переменной только постоянный (существующий) номер строки. Однако впоследствии можно было случайно обработать эту переменную как целое число, например, увеличить ее, что привело к неопределенному поведению в gototime. Следующий код демонстрирует поведение goto i, когда строка iне указана:

присвоить 200 i i = i + 1 goto i! неопределенное поведение 200 write (*, *) «это допустимый номер строки»

Некоторые компиляторы C реализуют два нестандартных расширения C / C ++, относящихся к gotos, изначально представленных gcc. Расширение GNU позволяет получить адрес метки внутри текущей функции как void *с использованием унарного префикса оператора значения метки . Инструкция goto также расширена, чтобы разрешить переход к произвольному выражению void *. Это расширение C называется вычисляемым goto в документации поддерживающих его компиляторов C; его семантика является надмножеством назначенного Фортрана goto, потому что он допускает произвольные выражения указателя в качестве цели перехода, в то время как назначенный Фортран goto не допускает произвольных выражений в качестве цели перехода. Как и в случае со стандартным goto в C, расширение GNU C позволяет целевому объекту вычисляемого goto находиться только в текущей функции. Попытка перейти за пределы текущей функции приводит к неопределенному поведению.

Некоторые варианты BASIC также поддерживают вычисляемый GOTO в том смысле, который используется в GNU C, то есть в котором целью может быть любой номер строки, а не только одна из список. Например, в MTS BASIC можно написать GOTO i * 1000, чтобы перейти к строке, пронумерованной в 1000 раз больше значения переменной i (которая может представлять выбранный параметр меню, например Переменные метки

PL / I достигают эффекта вычисленных или назначенных GOTO.

ALTER

. До стандарта ANSI COBOL 1985 года имелась команда ALTER, которая могла использоваться для изменения места назначения существующего GO TO, который должен был быть в абзац сам по себе. Функция, которая допускала полиморфизм, часто осуждалась и использовалась редко.

Perl GOTO

В Perl есть вариант goto, который вообще не является традиционным оператором GOTO. Он принимает имя функции и передает управление, эффективно заменяя один вызов функции другим (хвостовой вызов ): новая функция не вернется в GOTO, а вместо этого вернется в то место, из которого была вызвана исходная функция..

Эмуляция GOTO

Есть несколько языков программирования, которые по умолчанию не поддерживают GOTO. Используя эмуляцию GOTO, все еще можно использовать GOTO на этих языках программирования, хотя и с некоторыми ограничениями. Можно эмулировать GOTO в Java, JavaScript и Python.

Переменные метки PL / I

PL / I имеют тип данных LABEL, который может использоваться для реализации как «назначенного перехода» и «вычисленный goto». PL / I разрешает ветви вне текущего блока. Вызывающая процедура может передать метку в качестве аргумента вызываемой процедуре, которая затем может выйти с ветвью. Значение переменной метки включает в себя адрес кадра стека, а переход из блока выталкивает стек.

/ * Это реализует эквивалент * / / * назначенного goto * / declare where label; где = где-то; перейти куда;... где-то: / * оператор * /;...
/ * Это реализует эквивалент * / / * вычисленного goto * / declare where (5) label; объявить inx фиксированным; где (1) = abc; где (2) = xyz;... перейти туда, где (inx);... abc: / * оператор * /;... xyz: / * оператор * /;...

MS / DOS GOTO

Goto направляет выполнение на метку, которая начинается с двоеточия. Цель Goto может быть переменной.

@echo off SET D8str =% date% SET D8dow =% D8str: ~ 0,3% FOR %% D в (понедельник, среда, пятница) делать, если "%% D" == "% D8dow%" goto SHOP% % D echo Сегодня,% D8dow%, не день покупок. goto end: SHOPMon echo купить пиццу на обед - понедельник - день пиццы. goto end: SHOPWed echo купить Calzone, чтобы забрать домой - сегодня среда. goto end: SHOPFri echo покупает Зельцер на случай, если кто-то захочет напиток с нулевой калорийностью. : end
Поддержка языков

Многие языки поддерживают оператор goto, а многие нет. В Java, gotoявляется зарезервированным словом, но его нельзя использовать, хотя скомпилированный file.class генерирует GOTO и LABEL. Python не поддерживает goto, хотя есть несколько модулей-шуток, которые ее предоставляют. В Seed7 нет оператора goto, и скрытые операторы goto, такие как операторы break и continue, также опускаются. В PHP не было встроенной поддержки gotoдо версии 5.3 (были доступны библиотеки для эмуляции его функциональности).

язык программирования C # имеет goto. Однако он не позволяет переходить к метке за пределами текущей области видимости, что делает его значительно менее мощным и опасным, чем ключевое слово gotoв других языках программирования. Он также создает метки операторов case и default, область действия которых - включающий оператор switch ; goto case или goto default часто используются в качестве явной замены неявного перехода, который запрещен в C #.

В других языках могут быть свои собственные отдельные ключевые слова для явных провалов, которые можно рассматривать как версию goto, ограниченную этой конкретной целью. Например, Go использует ключевое слово fallthroughи вообще не допускает неявного падения, в то время как Perl 5 использует nextдля явного падения по умолчанию, но также позволяет установить неявное падение как поведение по умолчанию. для модуля.

Большинство языков, в которых есть операторы goto, называют это так, но на заре компьютерных технологий использовались другие имена. Например, в MAD использовался оператор TRANSFER TO. APL использует стрелку вправо, для перехода.

В C есть goto, и он обычно используется в различных идиомах, как обсуждалось выше.

В Perl также есть функция goto.

Функциональные языки программирования, такие как Scheme, обычно не имеют goto, вместо этого используются продолжения.

См. Также
Ссылки
Последняя правка сделана 2021-05-22 14:56:23
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте