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

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

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

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

12 ответов

Решение

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

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

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 в основном просто указатели, строки с нулевым символом в конце почти так же просто).

Управляющие структуры

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

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

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

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

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

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

Для интересного описания истории языка, см . Развитие языка C - Деннис Ричи

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

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

int myInt = 10;

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

myInt dw 1
mov myInt,10

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

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. Даже если вы вырезаете все остальные вещи и просто сравниваете C "main" с C++ "main", есть много лишних вещей.

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

Там цитата:

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

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

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

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

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

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

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

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

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

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

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

Язык ассемблера процесса, как правило, имеет дело с переходом (перейти к), инструкциями, операторами перемещения, двоичным артритом (XOR, NAND, AND OR и т. Д.), Полями памяти (или адресом). Распределяет память по двум типам: инструкциям и данным. Это почти все на языке ассемблера (я уверен, что программисты на ассемблере будут утверждать, что в этом есть нечто большее, но это сводится к этому в целом). С очень напоминает эту простоту.

С собирать то, что алгебра для арифметики.

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

Остерегайтесь вводящих в заблуждение сравнений

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Это утверждение просто означает, что структуры данных и управления в 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 что согласуется с форматом с плавающей запятой, поддерживаемым процессором, не только потому, что оба были согласованы, но и потому, что существует независимый стандарт для этого представления.

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

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

  • Исключения

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам