Я не впевнений, але думаю, що відповідь - ні, з досить тонких причин. Я запитав теоретичні інформатики кілька років тому і не отримав відповіді, що виходить за рамки того, що я тут представлю.
У більшості мов програмування ви можете імітувати машину Тюрінга:
- моделювання скінченного автомата з програмою, яка використовує кінцевий об'єм пам'яті;
- моделювання стрічки з парою пов'язаних списків цілих чисел, що представляють вміст стрічки до і після поточного положення. Переміщення вказівника означає перенесення заголовка одного зі списків на інший список.
Конкретна реалізація, що працює на комп’ютері, втратила б пам’ять, якщо стрічка вийшла занадто довгою, але ідеальна реалізація могла б сумлінно виконати програму машини Тьюрінга. Це можна зробити за допомогою ручки та паперу або придбати комп’ютер з більшою кількістю пам’яті та компілятор, орієнтований на архітектуру з більшою кількістю бітів на слово тощо, якщо у програми колись не вистачає пам’яті.
У C це не працює, оскільки неможливо мати зв’язаний список, який може зростати назавжди: завжди існує певна обмеженість у кількості вузлів.
Щоб пояснити чому, спершу потрібно пояснити, що таке реалізація на C. С насправді сімейство мов програмування. Стандарт ISO C (точніше, конкретна версія цього стандарту) визначає (з рівнем формальності, який дозволяє англійська) синтаксис та семантику сімейства мов програмування. C має багато невизначеної поведінки та визначеної реалізацією поведінки. "Реалізація" C кодифікує всю поведінку, визначену реалізацією (перелік речей, які потрібно кодифікувати, є в додатку J до C99). Кожна реалізація C є окремою мовою програмування. Зауважте, що значення слова "реалізація" є дещо своєрідним: що це насправді означає мовний варіант, може бути кілька різних програм компілятора, які реалізують один і той же варіант мови.
2CHAR_BITt
2CHAR_BIT×sizeof(t)
2CHAR_BIT×sizeof(void*)
Ці значення CHAR_BIT
та sizeof(void*)
видимі, тому якщо у вас не вистачає пам'яті, ви не можете просто відновити роботу програми з більшими значеннями для цих параметрів. Ви б запускали програму під іншою мовою програмування - іншою реалізацією на C.
n×2CHAR_BIT×sizeof(void*)n
C не нав'язує безпосередньо максимальну глибину рекурсії. Дозволено мати максимум, але також не можна. Але як ми спілкуємося між викликом функції та його батьківським? Аргументи не корисні, якщо вони адресовані, тому що це побічно обмежувало б глибину рекурсії: якщо у вас є функція, int f(int x) { … f(…) …}
то всі входження x
в активних кадрах f
мають свою власну адресу, і тому кількість вкладених викликів обмежується числом можливих адрес для x
.
Програма змінного струму може використовувати не адресований сховище у вигляді register
змінних. "Нормальні" реалізації можуть мати лише невелику, обмежену кількість змінних, що не мають адреси, але теоретично реалізація може забезпечити необмежену кількість register
пам'яті. У такій реалізації ви можете робити необмежену кількість рекурсивних викликів функції, доки є її аргументом register
. Але оскільки аргументи є register
, ви не можете зробити вказівник на них, і тому вам потрібно копіювати їх дані навколо: ви можете передавати лише обмежений обсяг даних, а не структуру даних довільного розміру, яка складається з покажчиків.
З необмеженою глибиною рекурсії та обмеженням того, що функція може отримувати дані лише від свого прямого абонента ( register
аргументи) та повертати дані своєму прямому виклику (повернене значення функції), ви отримуєте потужність детермінованих автоматичних натискань .
Я не можу знайти спосіб піти далі.
(Звичайно, ви можете змусити програму зберігати вміст стрічки зовнішньо через функції введення / виводу файлів. Але тоді ви не запитаєте, чи є C повним Тюрінгом, але чи C безмежною системою зберігання є Тюрінг-повною, відповідь - нудне "так". Ви можете також визначити, що сховище буде оракулом Тюрінга, дзвінок fopen("oracle", "r+")
, fwrite
початковий вміст стрічки до нього та fread
повернення остаточного вмісту стрічки.)