Как типы данных C "поддерживаются непосредственно большинством компьютеров"?

Я читаю K & R "Язык программирования C" и наткнулся на это утверждение [Введение, стр. 3]:

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

Что означает выражение полужирным шрифтом? Есть ли пример типа данных или структуры управления, которая не поддерживается непосредственно компьютером?

Ответ 1

Да, типы данных не поддерживаются напрямую.

Во многих встроенных системах нет аппаратного модуля с плавающей запятой. Итак, когда вы пишете такой код:

float x = 1.0f, y = 2.0f;
return x + y;

Он преобразуется в нечто вроде этого:

unsigned x = 0x3f800000, y = 0x40000000;
return _float_add(x, y);

Затем компилятор или стандартная библиотека должны предоставить реализацию _float_add(), которая занимает память встроенной системы. Если вы считаете байты на действительно крошечной системе, это может скомпенсироваться.

Другим распространенным примером является 64-разрядные целые числа (long long в стандарте C с 1999 года), которые напрямую не поддерживаются 32-разрядными системами. Старые системы SPARC не поддерживали целочисленное умножение, поэтому умножение должно было быть обеспечено средой выполнения. Существуют и другие примеры.

Другие языки

Для сравнения, другие языки имеют более сложные примитивы.

Например, для символа Lisp требуется много поддержки во время выполнения, как и таблицы в Lua, строки в Python, массивы в Fortran и т.д. Эквивалентные типы в C обычно либо не являются частью стандартной библиотеки вообще (нет стандартных символов или таблиц), либо они намного проще и не требуют большой поддержки во время выполнения (массивы в C в основном являются указателями, строки с nul-terminated почти так же просто).

Структуры управления

Известная структура управления, отсутствующая на C, является обработкой исключений. Нелокальный выход ограничен setjmp() и longjmp(), которые просто сохраняют и восстанавливают определенные части состояния процессора. Для сравнения, среда выполнения С++ должна пройти стек и вызвать деструкторы и обработчики исключений.

Ответ 2

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

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

Авторы C-языка – и языки B и BCPL, которые непосредственно предшествовали ему; были намерены определить конструкции на языке, которые были как можно более эффективно скомпилированы в Ассамблею... на самом деле они были вынуждены ограничить целевое оборудование. Как указывали другие ответы, это включали ветки (GOTO и другое управление потоком в C), перемещение (назначение), логические операции (& | ^), базовые арифметические (добавление, вычитание, приращение, декремент) и адресация памяти ( указатели). Хорошим примером являются операторы pre/post-increment и decment в C, которые предположительно были добавлены на язык B Кеном Томпсоном специально потому, что они были способны переводить непосредственно на один код операции после компиляции.

Это то, что авторы имели в виду, когда говорили, что "поддерживаются непосредственно большинством компьютеров". Они не означали, что другие языки содержали типы и структуры, которые поддерживались напрямую не - это означало, что конструкторы C, переведенные наиболее непосредственно (иногда буквально напрямую) в Assembly.

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

За интересной записью истории языка см. Развитие языка C - Деннис Ричи

Ответ 3

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

Более длинный ответ требует немного знания языка ассемблера. В C это утверждение такое:

int myInt = 10;

переведет что-то подобное в сборке:

myInt dw 1
mov myInt,10

Сравните это с чем-то вроде С++:

MyClass myClass;
myClass.set_myInt(10);

Полученный код языка ассемблера (в зависимости от того, насколько велика MyClass()), может содержать до сотни строк языка ассемблера.

Без собственно создания программ на ассемблере, чистый C, вероятно, является "самым скудным" и "самым жестким" кодом, в который вы можете сделать программу.

ИЗМЕНИТЬ

Учитывая комментарии к моему ответу, я решил запустить тест только для моего собственного здравомыслия. Я создал программу под названием "test.c", которая выглядела так:

#include <stdio.h>

void main()
{
    int myInt=10;

    printf("%d\n", myInt);
}

Я скомпилировал это с помощью gcc. Я использовал следующую командную строку для ее компиляции:

gcc -S -O2 test.c

Вот результирующий язык ассемблера:

    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB24:
    .cfi_startproc
    movl    $10, %edx
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    jmp __printf_chk
    .cfi_endproc
.LFE24:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Затем я создаю файл под названием "test.cpp", который определяет класс и выводит то же самое, что и "test.c":

#include <iostream>
using namespace std;

class MyClass {
    int myVar;
public:
    void set_myVar(int);
    int get_myVar(void);
};

void MyClass::set_myVar(int val)
{
    myVar = val;
}

int MyClass::get_myVar(void)
{
    return myVar;
}

int main()
{
    MyClass myClass;
    myClass.set_myVar(10);

    cout << myClass.get_myVar() << endl;

    return 0;
}

Я скомпилировал его таким же образом, используя следующую команду:

g++ -O2 -S test.cpp

Вот результирующий файл сборки:

    .file   "test.cpp"
    .section    .text.unlikely,"ax",@progbits
    .align 2
.LCOLDB0:
    .text
.LHOTB0:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9set_myVarEi
    .type   _ZN7MyClass9set_myVarEi, @function
_ZN7MyClass9set_myVarEi:
.LFB1047:
    .cfi_startproc
    movl    %esi, (%rdi)
    ret
    .cfi_endproc
.LFE1047:
    .size   _ZN7MyClass9set_myVarEi, .-_ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE0:
    .text
.LHOTE0:
    .section    .text.unlikely
    .align 2
.LCOLDB1:
    .text
.LHOTB1:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9get_myVarEv
    .type   _ZN7MyClass9get_myVarEv, @function
_ZN7MyClass9get_myVarEv:
.LFB1048:
    .cfi_startproc
    movl    (%rdi), %eax
    ret
    .cfi_endproc
.LFE1048:
    .size   _ZN7MyClass9get_myVarEv, .-_ZN7MyClass9get_myVarEv
    .section    .text.unlikely
.LCOLDE1:
    .text
.LHOTE1:
    .section    .text.unlikely
.LCOLDB2:
    .section    .text.startup,"ax",@progbits
.LHOTB2:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1049:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $10, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1049:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .text.unlikely
.LCOLDB3:
    .section    .text.startup
.LHOTB3:
    .p2align 4,,15
    .type   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, @function
_GLOBAL__sub_I__ZN7MyClass9set_myVarEi:
.LFB1056:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1056:
    .size   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, .-_GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Как вы можете ясно видеть, результирующий файл сборки намного больше в файле С++, затем он находится в файле C. Даже если вы вырезаете все остальное и просто сравните C "main" с С++ "main", есть много лишних вещей.

Ответ 4

K & R означает, что большинство выражений C (технический смысл) сопоставляются с одной или несколькими инструкциями по сборке, а не вызовом функции в библиотеку поддержки. Обычными исключениями являются целочисленное деление на архитектуре без инструкции аппаратного div или с плавающей запятой на машинах без FPU.

Вот цитата:

C сочетает гибкость и мощь языка ассемблера с удобством использования языка ассемблера.

(найдено здесь. Мне показалось, что я помню другое изменение, такое как "скорость ассемблера с удобством и выразительностью языка ассемблера". )

long int обычно имеет ту же ширину, что и собственные машинные регистры.

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

Если вы хотите работать с 128-битными ints на x86-64 или в общем случае BigInteger произвольного размера, вам нужна библиотека функций для него. Все процессоры теперь используют дополнение 2s в качестве двоичного представления отрицательных целых чисел, но даже это было не так, когда C был спроектирован. (Вот почему некоторые вещи, которые давали бы разные результаты для машин без 2-х дополнителей, технически undefined в стандартах C.)

C указатели на данные или функции работают так же, как адреса ассемблера.

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

Строки - это просто массивы

Помимо функций библиотеки, единственными строковыми операциями являются чтение/запись символа. Нет concat, нет подстроки, нет поиска. (Строки хранятся в виде nul-terminated ('\0') массивов из 8-битных целых чисел, а не указателя + длины, поэтому для получения подстроки вам нужно записать нуль в исходную строку.)

Процессоры иногда имеют инструкции, предназначенные для использования функцией строкового поиска, но все же обычно обрабатывают один байт на каждую выполненную команду в цикле. (или с префиксом репрезентативности x86. Возможно, если C был спроектирован на x86, строковый поиск или сравнение был бы нативной операцией, а не вызовом библиотечной функции.)

Многие другие ответы приводят примеры вещей, которые не поддерживаются изначально, такие как обработка исключений, хеш-таблицы, списки. Философия дизайна K & R - причина, по которой C не имеет ни одного из них.

Ответ 5

Язык ассемблера процесса обычно имеет дело с прыжком (go to), операторами, операторами перемещения, бинарным артритом (XOR, NAND, AND OR и т.д.), полями памяти (или адресом). Классифицирует память на два типа, инструкцию и данные. Это касается всего языка ассемблера (я уверен, что программисты на сборках будут утверждать, что в этом есть нечто большее, но это сводится к этому вообще). C очень напоминает эту простоту.

C состоит в том, чтобы собрать алгебру в арифметику.

C инкапсулирует основы сборки (язык процессора). Вероятно, это более верное утверждение, чем "Поскольку типы данных и структуры управления, предоставляемые C, поддерживаются непосредственно большинством компьютеров"

Ответ 6

Остерегайтесь ошибочных сравнений

  • Утверждение основывается на понятии "библиотеки времени выполнения" , которая в большинстве своем вышла из моды, поскольку, по крайней мере, для основных языков высокого уровня. (Это по-прежнему актуально для самых маленьких встроенных систем.) Время выполнения - это минимальная поддержка, которую программа на этом языке требует выполнения, когда вы используете только конструкции, встроенные в язык (в отличие от явного вызова функции, предоставляемой библиотекой).
  • В отличие от этого, современные языки не склонны различать время выполнения и стандартную библиотеку, причем последние часто довольно обширны.
  • Во время книги K & R C даже не имела стандартной библиотеки. Скорее, доступные библиотеки C немного отличались между различными вариантами Unix.
  • Для понимания утверждения, которое вы не должны сравнивать с языками со стандартной библиотекой (например, Lua и Python, упомянутыми в других ответах), но на языках с более встроенными конструкциями (например, день LISP и старый FORTRAN, упомянутый в других ответах). Другими примерами будут BASIC (интерактивные, например, LISP) или PASCAL (скомпилированные, как FORTRAN), которые имеют (помимо прочего) функции ввода/вывода, встроенные прямо в язык.
  • Напротив, нет стандартного способа получить результаты вычислений из программы на C, которая использует только время выполнения, а не любую библиотеку.

Ответ 7

Есть ли пример типа данных или структуры управления, которая не является поддерживается непосредственно компьютером?

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

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

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

Такие типы данных и операции включают в себя:

  • манипулирование текстовой строкой произвольной длины - конкатенация, подстрока, назначение новой строки переменной, инициализированной некоторой другой строкой, и т.д. (= "Hello World!"; s = (s + s) [2: -2 ] 'в Python)
  • наборы
  • объекты с вложенными виртуальными деструкторами, как на С++ и на любой другой язык объектно-ориентированного программирования
  • 2D-матричное умножение и деление; решение линейных систем ( "C = B/A; x = A\b" в MATLAB и многих языках программирования массивов)
  • регулярные выражения
  • массивы переменной длины - в частности, добавление элемента в конец массива, который (иногда) требует выделения большего объема памяти.
  • чтение значения переменных, которые изменяют тип во время выполнения - иногда это float, иначе это строка
  • ассоциативные массивы (часто называемые "картами" или "словари" )
  • списки
  • ratio ( "(+ 1/3 2/7)" дает "13/21" в Lisp)
  • произвольная точность арифметики (часто называемой "bignums" )
  • преобразование данных в печатное представление (метод ".tostring" в JavaScript)
  • насыщение чисел с фиксированной точкой (часто используется во встроенных программах C)
  • оценка строки, введенной во время выполнения, как если бы это выражение ( "eval()" на многих языках программирования).

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

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

  • закрытие
  • продолжениями
  • исключения
  • ленивая оценка

Если он написан на С или на каком-либо другом языке, когда программа управляет такими типами данных, ЦП должен в конечном итоге выполнить любые инструкции, необходимые для управления этими типами данных. Эти инструкции часто содержатся в "библиотеке". Каждый язык программирования, даже C, имеет "библиотеку времени выполнения" для каждой платформы, которая включена по умолчанию в каждом исполняемом файле.

Большинство людей, которые пишут компиляторы, ставят инструкции для управления всеми типами данных, которые "встроены в язык" в свою библиотеку времени выполнения. Поскольку C не имеет ни одного из вышеуказанных типов данных и операций и структур управления, встроенных в язык, ни один из них не включен в библиотеку времени выполнения C, что делает библиотеку времени выполнения C меньшей, чем run- временную библиотеку других языков программирования, которые имеют более сложный материал, встроенный в язык.

Когда программист хочет, чтобы программа - на C или любой другой язык по своему выбору - манипулировала другими типами данных, которые не были "встроены в язык", этот программист обычно сообщает компилятору включать в него дополнительные библиотеки с этой программой, или иногда (чтобы "избегать зависимостей" ) записывает еще одну реализацию этих операций непосредственно в программе.

Ответ 8

Каковы встроенные типы данных в C? Это такие вещи, как int, char, * int, float, массивы и т.д.... Эти типы данных понимаются CPU. ЦП знает, как работать с массивами, как указатели разыменования и как выполнять арифметику на указатели, целые числа и числа с плавающей запятой.

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

Ответ 9

Это зависит от компьютера. На PDP-11, где был изобретен C, long был плохо поддержан (был добавлен дополнительный модуль расширения, который вы могли купить, который поддерживал некоторые, но не все 32-разрядные операции). То же самое относится к разным степеням в любой 16-разрядной системе, включая оригинальный IBM PC. А также для 64-разрядных операций на 32-битных машинах или в 32-битных программах, хотя язык C во время книги K & R вообще не имел 64-битных операций. И, конечно, на протяжении 80-х и 90-х годов было множество систем [включая 386 и около 486 процессоров] и даже некоторые встроенные системы, которые не поддерживали прямую арифметику с плавающей запятой (float или double).

Для более экзотического примера некоторые компьютерные архитектуры поддерживают только "ориентированные на слова" указатели (указывающие на двухбайтовое или четырехбайтное целое число в памяти), а указатели байтов (char * или void *) должны были быть реализовано добавлением дополнительного поля смещения. Этот вопрос подробно описывает такие системы.

Функции "библиотеки времени выполнения", к которым они относятся, относятся не к тем, которые вы увидите в руководстве, но такие функции, как это, в современном компиляторе runtime library, которые используются для реализации операций базового типа, которые не поддерживаются машиной. Библиотека времени выполнения, к которой относятся K & R, можно найти на Веб-сайте Общества Unix Heritage - вы можете видеть такие функции, как ldiv ( отличная от функции C с тем же именем, которая не существовала в то время), которая используется для реализации деления 32-битных значений, которые PDP-11 не поддерживал даже с надстройкой, и csv ( и cret также в csv.c), которые сохраняют и восстанавливают регистры в стеке для управления вызовами и возврата из функций.

Они, вероятно, также ссылались на их выбор, чтобы не поддерживать многие типы данных, которые напрямую не поддерживаются базовой машиной, в отличие от других современных языков, таких как FORTRAN, которые имели семантику массива, которая также не отображалась в ЦП, лежащем в основе поддержка указателей как массивы C. Тот факт, что массивы C всегда имеют нуль-индексирование и всегда имеют известный размер во всех рангах, но первое означает, что нет необходимости хранить диапазоны индексов или размеры массивов и нет необходимости иметь функции библиотеки времени выполнения для их доступа - компилятор может просто жестко закодировать необходимую арифметику указателя.

Ответ 10

Утверждение просто означает, что структуры данных и управления в C являются машино-ориентированными.

Здесь есть два аспекта. Во-первых, язык C имеет определение (стандарт ISO), которое позволяет определять ширину типов данных. Это означает, что реализации языка C адаптированы к машине. Типы данных компилятора C соответствуют тому, что доступно в машине, на которой нацелен компилятор, потому что язык имеет широту для этого. Если машина имеет необычный размер слова, например 36 бит, то тип int или long можно сделать так, чтобы соответствовать этому. Программы, предполагающие, что int ровно 32 бита, будут разбиты.

Во-вторых, из-за таких проблем с переносимостью возникает второй эффект. В некотором смысле утверждение в K & R стало своего рода самореализующимся пророчеством или, возможно, наоборот. То есть, разработчики новых процессоров знают о необходимости поддержки C-компиляторов, и они знают, что существует много кода C, который предполагает, что "каждый процессор выглядит как 80386". Архитектура спроектирована с учетом C: и не только C, но и с общими заблуждениями относительно C-переносимости. Вы просто не можете ввести машину с 9-битными байтами или что-то еще для общего использования. Программы, предполагающие, что тип char имеет ровно 8 бит ширины, сломается. Будут продолжаться только некоторые программы, написанные специалистами по переносимости: вряд ли хватит, чтобы свести разумные усилия к полной системе с инструментальной цепочкой, ядром, пользовательским пространством и полезными приложениями. Другими словами, типы C выглядят так, как доступно на аппаратном обеспечении, потому что аппаратное обеспечение было похоже на какое-то другое оборудование, для которого было написано много непортативных программ на C.

Есть ли пример типа данных или структуры управления, которая не поддерживается непосредственно компьютером?

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

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

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

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

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

Кроме того, говоря о плавающей запятой, существует стандартизация: плавающая точка IEEE 754. Почему ваш компилятор C имеет double, который согласуется с форматом с плавающей запятой, поддерживаемым процессором, заключается не только в том, что они были согласны, а потому, что для этого представления существует независимый стандарт.

Ответ 11

Такие вещи, как

  • Списки Используется практически во всех функциональных языках.

  • <сильные > Исключения.

  • Ассоциативные массивы (Карты) - включены в. PHP и Perl.

  • Сбор мусора.

  • Типы данных/структуры управления, включенные на многие языки, но не поддерживаемые напрямую ЦП.

Ответ 12

Поддерживаемые напрямую следует понимать как эффективное отображение в набор команд процессора.

  • Прямая поддержка целых типов - это правило, за исключением длинных (может потребоваться расширенная арифметическая процедура) и коротких размеров (может потребоваться маскировка).

  • Прямая поддержка типов с плавающей точкой требует наличия FPU.

  • Непосредственная поддержка битовых полей является исключительной.

  • Структуры и массивы требуют вычисления адресов, которые в некоторой степени поддерживаются.

  • Указатели всегда поддерживаются напрямую с помощью косвенной адресации.

  • goto/if/while/for/do напрямую поддерживаются безусловными/условными ветвями.

  • переключатель может поддерживаться непосредственно при использовании таблицы перехода.

  • Функциональные вызовы напрямую поддерживаются с помощью функций стека.