Як типи даних C "підтримуються безпосередньо більшістю комп'ютерів"?


114

Я читаю K & R «Мова програмування на C» і стикався з цим твердженням [Вступ, с. 3]:

Оскільки типи даних і структури управління, що надаються C, підтримуються безпосередньо більшістю комп'ютерів , бібліотека часу виконання, необхідна для реалізації автономних програм, є невеликою.

Що означає жирне твердження? Чи є приклад типу даних або структури управління, яка не підтримується безпосередньо комп’ютером?


1
У наші дні мова С підтримує складну арифметику, але спочатку це не було, оскільки комп'ютери не підтримують безпосередньо складні числа як типи даних.
Джонатан Леффлер

12
Насправді історично було навпаки: C розроблявся з апаратних операцій та типів, наявних на той час.
Базиль Старинкевич

2
Більшість комп’ютерів не мають прямої апаратної підтримки для десяткових плавців
PlasmaHH

3
@MSalters: Я намагався натякнути на якийсь напрямок для питання "Чи є приклад типу даних або структури управління, яка не підтримується безпосередньо комп'ютером?" який я не тлумачив як обмеження K&R
ПлазмаHH

11
Як це не дублікат понад 6 років після запуску пакету Overflow
Пітер Мортенсен

Відповіді:


143

Так, існують типи даних, які безпосередньо не підтримуються.

У багатьох вбудованих системах немає апаратного блоку з плаваючою точкою. Отже, коли ви пишете такий код:

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, et cetera. Еквівалентні типи в C зазвичай взагалі або не є частиною стандартної бібліотеки (немає стандартних символів або таблиць), або вони набагато простіші і не потребують великої підтримки часу виконання (масиви на C - це в основному лише вказівники, рядки, що закінчуються нулем, є майже так само просто).

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

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


2
в основному просто вказівники ... швидше, в основному просто сирі шматки пам'яті. Навіть якщо це збирання азоту, і відповідь все одно хороша.
Дедуплікатор

2
Ви можете стверджувати, що нульові завершені рядки мають "апаратну підтримку", оскільки термінатор рядків відповідає операції "стрибок, якщо нуль" більшості процесорів, і, таким чином, трохи швидше, ніж інші можливі реалізації рядків.
Петріс

1
Опублікував свою власну відповідь, щоб розширити те, як C призначений для простого відображення зображення.
Пітер Кордес

1
Будь ласка, не використовуйте колокацію "масиви в основному є лише вказівниками", це може серйозно, сильно ввести в оману такого початківця, як ОП. Щось уздовж рядків "масиви безпосередньо реалізуються за допомогою покажчиків на апаратному рівні" було б краще ІМО.
Парамагнітний круасан

1
@TheParamagneticCroissant: Я думаю, що в цьому контексті це доречно ... ясність йде ціною точності.
Дітріх Епп

37

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

Комп'ютери - це лише банки пам'яті та центральні процесори, і кожен процесор працює за допомогою машинного коду; Частина дизайну кожного процесора - це архітектура наборів інструкцій, яка називається Assembly Assembly , яка відображає один на один із набору мнемоніки, читаної людиною, до машинного коду, який є всіма цифрами.

Автори мови С - і мов B та BCPL, які безпосередньо передували їй - мали намір визначити конструкції мовою, які були максимально ефективно складені в Асамблею ... насправді, вони були змушені обмеженнями в цілі обладнання. Як вказували інші відповіді, це включало гілки (GOTO та інше управління потоком у C), переміщення (призначення), логічні операції (& | ^), основні арифметичні (додавання, віднімання, збільшення, зменшення) та адресацію пам'яті (покажчики ). Хорошим прикладом є оператори до / після збільшення та зменшення в C, які нібито були додані К мовою Кем Томпсоном спеціально тому, що вони змогли перевести безпосередньо в єдиний код, щойно був складений.

Це мали на увазі автори, сказавши, що "підтримується безпосередньо більшістю комп'ютерів". Вони не означали, що інші мови містять типи та структури, які не підтримуються безпосередньо - вони мали на увазі, що конструкції C конструкти переводяться найбільш безпосередньо (іноді буквально безпосередньо) в Асамблею.

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

Для цікавого пишуть-ап історії мови, см Розвиток Мова оригіналу - Денніс Рітчі


14

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

Більш довга відповідь вимагає трохи знання мовної збірки. В C таке твердження:

int myInt = 10;

перекладав би щось подібне в зборах:

myInt dw 1
mov myInt,10

Порівняйте це з чимось на зразок C ++:

MyClass myClass;
myClass.set_myInt(10);

Отриманий код мови збірки (залежно від того, наскільки великий MyClass ()), може скласти до сотень ліній мови збірки.

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

EDIT

З огляду на коментарі до моєї відповіді, я вирішив пройти тест, тільки заради власної розумності. Я створив програму під назвою "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", є багато зайвих матеріалів.


14
Цей "C ++ код" просто не є C ++. І справжній код, такий як MyClass myClass { 10 }у C ++, дуже ймовірно, складеться саме на ту ж збірку. Сучасні компілятори C ++ усунули покарання абстракції. І як результат, вони часто можуть обіграти компілятори C. Наприклад, покарання за абстракцію в C qsortє справжнім, але після + std::sortбазової оптимізації C ++ не має покарання за абстракцію.
MSalters

1
За допомогою IDA Pro ви можете легко побачити, що більшість конструкцій C ++ складаються на те саме, що робити це вручну в C, конструктори та диктори накреслюються для тривіальних об’єктів, тоді застосовується майбутня оптимізація
паульма

7

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

Є цитата:

C поєднує гнучкість та потужність мови складання з зручністю користувачу мовою складання.

( знайдено тут . Я думав, що запам'ятав іншу варіацію, наприклад, "швидкість мови складання із зручністю та виразністю мови збірки".)

long int, як правило, такої ж ширини, що і реєстри нашої машини.

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

Якщо ви хочете працювати з 128-бітовими входами на x86-64, або в загальному випадку BigInteger довільного розміру, вам потрібна бібліотека функцій для нього. Зараз усі процесори використовують доповнення 2s як двійкове представлення від'ємних цілих чисел, але навіть це не було в тому випадку, коли C був розроблений. (Ось чому деякі речі, які давали б різні результати на машинах, що не доповнюють 2, технічно не визначені у стандартах С.)

C покажчики даних або функцій працюють так само, як адреси збирання.

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

Рядки - це просто масиви

Поза межами функцій бібліотеки єдиними наданими рядковими операціями є читання / запис символу. Ні лаконічності, ні підрядки, ні пошуку. (Рядки зберігаються як скасовані на нуль ('\0' масивів з 8-бітовими цілими числами, що не нулем, а не вказівник + довжина, тому для отримання підрядка вам доведеться записати нуль у вихідний рядок.)

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

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


"K&R означає, що більшість виразів C (технічного значення) позначають одну або кілька інструкцій зі збирання, а не виклик функції до бібліотеки підтримки." Це дуже інтуїтивне пояснення. Дякую.
gwg

1
Я щойно натрапив на термін "мова Неймана" ( en.wikipedia.org/wiki/Von_Neumann_programming_languages ). ТОЧНО, що це C.
Пітер Кордес

1
Саме тому я використовую C. Але те, що мене здивувало, коли я навчаюсь C, це те, що намагаючись бути ефективним для широкого кола апаратних засобів, це часом неефективно і неефективно для більшості сучасних апаратних засобів. Я маю на увазі, наприклад, відсутність корисного і надійного способу виявлення цілого числа-переповнення-в-с та багатословного додавання-використання-перенесення-прапора .
Z boson

6

Мова складання процесу зазвичай стосується стрибків (перехід до), операторів, операторів переміщення, двійкових артритів (XOR, NAND і АБО тощо), полів пам'яті (або адреси). Класифікує пам'ять на два типи, інструкції та дані. Тобто про всю мову монтажу є (я впевнений, програмісти зі зборки стверджують, що тут є більше, ніж це, але загалом це зводиться). C дуже нагадує цю простоту.

C - зібрати, що алгебра арифметична.

C інкапсулює основи складання (мова процесора). Це, мабуть, вірніше твердження, ніж "Тому що типи даних та структури управління, що надаються C, підтримуються безпосередньо більшості комп'ютерів"


5

Остерігайтеся оманливих порівнянь

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

З іншого боку, більшість сучасних мов функціонують у спеціалізованих середовищах виконання, які надають такі засоби, як сміття.
Nate CK

5

Чи є приклад типу даних або структури управління, яка не підтримується безпосередньо комп’ютером?

Усі основні типи даних та їх роботи на мові С можна реалізувати за допомогою однієї чи кількох машиномовних інструкцій без циклу - вони безпосередньо підтримуються (практично кожним) процесором.

Для декількох популярних типів даних та їх роботи потрібні десятки інструкцій на машинній мові, або потрібна ітерація певного циклу виконання або обох.

У багатьох мовах є спеціальний скорочений синтаксис для таких типів та їхніх операцій - використання таких типів даних на C зазвичай вимагає введення коду набагато більше.

Такі типи даних та операції включають:

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

Усі ці операції потребують десятків інструкцій на машинній мові або вимагають повторення циклу виконання майже на кожному процесорі.

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

  • закриття
  • продовження
  • винятки
  • лінива оцінка

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

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

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


Якщо ваша реалізація Lisp оцінює (+ 1/3 2/7) як 3/21, я думаю, ви повинні мати особливо креативну реалізацію ...
RobertB

4

У чому вбудовані типи даних C? Вони такі речі , як int, char, * int,float , масиви і т.д. ... Ці типи даних розуміються ЦП. Центральний процесор знає, як працювати з масивами, як орієнтувати покажчики та як виконувати арифметику на покажчики, цілі числа та числа з плаваючою комою.

Але коли ви переходите на мови програмування вищого рівня, ви вбудовуєте абстрактні типи даних та більш складні конструкції. Наприклад, подивіться на величезний масив вбудованих класів на мові програмування C ++. Процесор не розуміє класів, об'єктів або абстрактних типів даних, тому час роботи C ++ усуває розрив між процесором та мовою. Це приклади типів даних, які не підтримуються безпосередньо більшості комп'ютерів.


2
x86 знає працювати з деякими масивами, але не з усіма. Для великих чи незвичних розмірів елементів знадобиться виконати цілу арифметику для перетворення індексу масиву в зміщення покажчика. А на інших платформах це завжди потрібно. І думка, що процесор не розуміє класів C ++, є смішною. Це просто зміщення покажчика, як C структури. Для цього вам не потрібно час виконання.
MSalters

@MSalters так, але фактичні методи стандартних бібліотечних класів, такі як iostreams тощо, - це функції бібліотеки, а не безпосередньо підтримувані компілятором. Однак мови вищого рівня, з якими вони, швидше за все, порівнювали це не з C ++, а з сучасними мовами, такими як FORTRAN та PL / I.
Випадково832

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

4

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

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

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

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


3

Заява просто означає, що структури даних та керування в С орієнтовані на машину.

Тут слід розглянути два аспекти. Одне полягає в тому, що мова С має визначення (стандарт ISO), яке дозволяє широту в визначенні типів даних. Це означає, що реалізація мови C адаптована до машини . Типи даних компілятора С відповідають тому, що є в машині, на яку компілятор націлений, оскільки мова має широту для цього. Якщо машина має незвичайний розмір слова, наприклад 36 біт, то тип intабо longйого можна зробити відповідно до цього. Програми, які передбачають, що intрівно 32 біти, будуть порушені.

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

Чи є приклад типу даних або структури управління, яка не підтримується безпосередньо комп’ютером?

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

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

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

C має деякі типові дані, які не підтримуються деякими машинами. Починаючи з C99, C має складні числа. Вони складаються з двох значень з плаваючою комою та створені для роботи з бібліотечними процедурами. Деякі машини взагалі не мають блоку з плаваючою комою.

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

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


2

Такі речі, як

  • Списки Використовуються майже у всіх функціональних мовах.

  • Винятки .

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

  • Збір сміття .

  • Типи даних / структури управління, що містяться на багатьох мовах, але не підтримуються ЦП безпосередньо.


2

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

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

  • Пряма підтримка типів з плаваючою комою вимагає наявності FPU.

  • Пряма підтримка бітових полів є винятковою.

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

  • Покажчики завжди безпосередньо підтримуються за допомогою непрямої адреси.

  • goto / if / while / for / do безпосередньо підтримується беззастережними / умовними гілками.

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.