C实际上是图灵完备的吗?


39

我试图向某人解释C是图灵完备的,并且意识到我实际上并不知道它在技术上确实是图灵完备的。(C代表抽象语义,而不是实际实现。)

就我所知,“明显”的答案(大致:它可以处理任意数量的内存,因此它可以仿真RAM机器,因此它是图灵完成的)实际上是不正确的,尽管C标准允许要使size_t任意大,必须将其固定为一定的长度,并且无论将其固定为多长都是有限的。(换句话说,尽管您可以给定一台任意停止的图灵机,选择一个size_t的长度以使其能够“正常”运行,但没有办法选择一个size_t的长度,以使所有停止的图灵机都能正常运行)

所以:C99 Turing完成了吗?


3
C的“抽象语义”是什么?他们在任何地方定义吗?
Yuval Filmus

4
@YuvalFilmus-参见例如此处,即标准中定义的C,而不是例如“这是gcc的工作方式”。
TLW

1
有一种“技术性”,即现代计算机没有像TM这样的无限内存,但仍然被认为是“通用计算机”。并请注意,编程语言的“语义”并没有真正假定有限的内存,只是它们的所有实现当然都限于内存。参见例如我们的PC是否用作图灵机。无论如何,基本上所有 “主流”编程语言都是图灵完整的。
vzn

2
C(像图灵机)不限于使用内部计算机存储器,用于其计算,所以这真的是不是针对C的图灵完全有效的参数
reinierpost

@reinierpost-就像说一个电传打字机很聪明。也就是说,“ C +外部TM等效”是图灵完成的,而不是C是图灵完成的。
TLW

Answers:


34

我不确定,但出于相当微妙的原因,我认为答案是否定的。几年前,我在《理论计算机科学》杂志上提出了一个问题,但没有得到超出我在这里要说的答案。

在大多数编程语言中,您可以通过以下方式模拟图灵机:

  • 用使用有限内存的程序模拟有限自动机;
  • 用一对整数链接列表模拟磁带,这些整数列表表示磁带在当前位置之前和之后的内容。移动指针意味着将其中一个列表的头转移到另一个列表上。

如果磁带太长,在计算机上运行的具体实现将用完内存,但是理想的实现可以忠实地执行Turing机器程序。这可以用笔和纸来完成,或者购买一台具有更多内存的计算机,以及针对每个单词有更多位的体系结构的编译器,以此类推,以防程序内存不足。

这在C语言中不起作用,因为不可能有一个永远增长的链表:节点数总是有一些限制。

为了解释为什么,我首先需要解释什么是C实现。C实际上是一门编程语言。在ISO C标准(更准确地说,这一标准的特定版本)定义(与正式的层次英语允许)的语法和语义家族编程语言。C有很多未定义行为实现定义行为。C的“实现”将所有实现定义的行为编码(要编码的事物列表在C99的附录J中)。C的每种实现都是单独的编程语言。请注意,“实现”一词的含义有点特殊:它的真正含义是语言变体,可以有多个不同的编译器程序实现相同的语言变体。

在C的给定实现中,字节具有可能的值。所有数据都可以表示为字节数组:类型最多具有 可能的值。这个数字在C的不同实现中有所不同,但是对于C的给定实现,它是一个常数。 2 CHAR_BIT × sizeof(t)2CHAR_BITt2CHAR_BIT×sizeof(t)

特别是,指针最多只能使用值。这意味着可寻址对象的最大数量是有限的。2CHAR_BIT×sizeof(void*)

的价值观CHAR_BITsizeof(void*)是可观的,所以如果你的内存用完了,你不能只是恢复与这些参数的值越大运行程序。您将使用另一种编程语言(另一种C实现)运行该程序。

如果某种语言的程序只能有一定数量的状态,则该编程语言仅比有限自动机更具表现力。限于可寻址存储的C片段最多只允许程序状态,其中是该对象的抽象语法树的大小。程序(代表控制流的状态),因此可以通过具有这么多状态的有限自动机来模拟该程序。如果C更具表达性,则必须通过使用其他功能来实现。 nn×2CHAR_BIT×sizeof(void*)n

C不直接施加最大递归深度。一个实现可以有一个最大值,但也可以没有一个最大值。但是,如何在函数调用与其父函数之间进行通信?如果参数是可寻址的,则不好,因为它会间接限制递归的深度:如果您有一个函数,int f(int x) { … f(…) …}x活动框架上所有出现的事件f都有其自己的地址,因此嵌套调用的数目受该数目的限制的可能地址x

AC程序可以使用register变量形式的不可寻址存储。“普通”实现只能有少量有限的没有地址的变量,但是从理论上讲,一个实现可以允许无限量的register存储。在这种实现中,只要函数的参数为​​,就可以无限制地对函数进行递归调用register。但是由于参数是register,您无法指向它们的指针,因此您需要显式地复制它们的数据:您只能传递有限数量的数据,而不能传递由指针组成的任意大小的数据结构。

借助无限制的递归深度,以及函数只能从其直接调用者(register参数)获取数据并将数据返回到其直接调用者(函数返回值)的限制,您将获得确定性下推自动机的强大功能。

我找不到进一步的方法。

(当然,您可以使程序通过文件输入/输出功能将磁带内容存储在外部。但是,您不必再问C是否是Turing-complete的,而是C加上无限存储系统是否是Turing-complete的)。答案是无聊的“是”。您还可以将存储定义为图灵预言机—调用  fopen("oracle", "r+")fwrite将初始的磁带内容fread返回到该磁带,然后返回最终的磁带内容。)


现在还不清楚为什么地址集应该是有限的。在回答您链接的问题时,我写了一些想法:cstheory.stackexchange.com/a/37036/43393
Alexey B.

4
抱歉,但是按照同样的逻辑,根本没有图灵完备的编程语言。每种语言都有对地址空间的显式或隐式限制。如果创建具有无限内存的计算机,则随机访问指针显然也将具有无限长度。因此,如果出现这样的机器,它将必须提供用于顺序存储器访问的指令集,以及用于高级语言的API。
IMil

14
@IMil这是不正确的。一些编程语言对地址空间没有限制,甚至没有隐式限制。举一个显而易见的例子,一台通用的图灵机(其中磁带的初始状态构成程序)是一种图灵完备的编程语言。实际上实际使用的许多编程语言都具有相同的属性,例如Lisp和SML。语言不必具有“随机访问指针”的概念。(续)
吉尔斯

11
@IMil (续)实现通常会提高性能,但是我们知道在特定计算机上运行的实现不是图灵完备的,因为它受计算机内存大小的限制。但这意味着该实现并不能实现整个语言,而仅是(在N字节内存中运行的程序的)子集。您可以在计算机上运行该程序,如果该程序内存不足,请将其移至更大的计算机上,依此类推,直到永久或停止为止。那将是实现整个语言的有效方法。
吉尔斯(Gillles)“所以-别再邪恶了”

6

C99 va_copy在可变参数API中的添加可能会给我们提供图灵完备性的后门。由于可以在一个函数(最初接收参数的函数除外)中多次遍历可变参数列表,va_args因此可用于实现无指针指针。

当然,可变参量API的真正实现可能在某个地方有一个指针,但是在我们的抽象机中,它可以使用魔术来实现。

这是一个使用任意转换规则实现2堆栈下推自动机的演示:

#include <stdarg.h>
typedef struct { va_list va; } wrapped_stack; // Struct wrapper needed if va_list is an array type.
#define NUM_SYMBOLS /* ... */
#define NUM_STATES /* ... */
typedef enum { NOP, POP1, POP2, PUSH1, PUSH2 } operation_type;
typedef struct { int next_state; operation_type optype; int opsymbol; } transition;
transition transition_table[NUM_STATES][NUM_SYMBOLS][NUM_SYMBOLS] = { /* ... */ };

void step(int state, va_list stack1, va_list stack2);
void push1(va_list stack2, int next_state, ...) {
    va_list stack1;
    va_start(stack1, next_state);
    step(next_state, stack1, stack2);
}
void push2(va_list stack1, int next_state, ...) {
    va_list stack2;
    va_start(stack2, next_state);
    step(next_state, stack1, stack2);
}
void step(int state, va_list stack1, va_list stack2) {
    va_list stack1_copy, stack2_copy;
    va_copy(stack1_copy, stack1); va_copy(stack2_copy, stack2);
    int symbol1 = va_arg(stack1_copy, int), symbol2 = va_arg(stack2_copy, int);
    transition tr = transition_table[state][symbol1][symbol2];
    wrapped_stack ws;
    switch(tr.optype) {
        case NOP: step(tr.next_state, stack1, stack2);
        // Note: attempting to pop the stack's bottom value results in undefined behavior.
        case POP1: ws = va_arg(stack1_copy, wrapped_stack); step(tr.next_state, ws.va, stack2);
        case POP2: ws = va_arg(stack2_copy, wrapped_stack); step(tr.next_state, stack1, ws.va);
        case PUSH1: va_copy(ws.va, stack1); push1(stack2, tr.next_state, tr.opsymbol, ws);
        case PUSH2: va_copy(ws.va, stack2); push2(stack1, tr.next_state, tr.opsymbol, ws);
    }
}
void start_helper1(va_list stack1, int dummy, ...) {
    va_list stack2;
    va_start(stack2, dummy);
    step(0, stack1, stack2);
}
void start_helper0(int dummy, ...) {
    va_list stack1;
    va_start(stack1, dummy);
    start_helper1(stack1, 0, 0);
}
// Begin execution in state 0 with each stack initialized to {0}
void start() {
    start_helper0(0, 0);
}

注意:如果va_list是数组类型,则实际上有指向函数的隐藏指针参数。因此,最好将所有va_list参数的类型更改为wrapped_stack


这可能有效。一个可能的问题是它依赖于分配无数个自动va_list变量stack。这些变量必须有一个address &stack,而我们只能有一个有限的数目。可以通过声明每个局部变量来规避此要求register,也许吗?

@chi AIUI变量不需要地址,除非有人尝试获取该地址。另外,在省略号之前声明参数是非法的register
feersum

出于同样的逻辑,int除非有人使用边界或sizeof(int)?,否则不应该不要求您有一个边界吗?

@chi一点也不。该标准定义了an int在某些有限范围INT_MIN和之间的值作为抽象语义的一部分INT_MAX。如果int溢出的值超出了这些限制,则会发生未定义的行为。另一方面,该标准有意不要求所有对象都物理存在于内存中的特定地址,因为这允许进行优化,例如将对象存储在寄存器中,仅存储对象的一部分,以不同于标准的方式表示对象布局,或者如果不需要则完全省略。
feersum

4

非标准算术,也许吗?

因此,问题似乎是的有限大小sizeof(t)。但是,我想我知道一种解决方法。

据我所知,C不需要实现将标准整数用于其整数类型。因此,我们可以使用非标准的算术模型。然后,我们将设置sizeof(t)为一些非标准数字,而现在我们将永远不会在有限的步骤中达到它。因此,图灵机磁带的长度将始终小于“最大”,因为从字面上看不可能达到最大。sizeof(t)根本不是通常意义上的数字。

这当然是一种技术性:Tennenbaum定理。它指出,Peano算术的唯一模型是标准模型,显然是不行的。但是,据我所知,C并不要求实现使用满足Peano公理的数据类型,也不要求实现是可计算的,因此这不应该成为问题。

如果尝试输出非标准整数应该怎么办?好吧,您可以使用非标准字符串表示任何非标准整数,因此只需从该字符串的开头流数字即可。


实现会是什么样子?
reinierpost

@reinierpost我猜想它将使用一些可数的非标准PA模型来表示数据。它将使用PA度来计算算术运算。我认为任何此类模型都应提供有效的C实现。
PyRulez

抱歉,这不起作用。sizeof(t)本身是类型的值size_t,因此它是介于0和之间的自然整数SIZE_MAX
吉尔斯(Gilles)'所以

那么@Gilles SIZE_MAX也是非标准的自然值。
PyRulez

这是一个有趣的方法。请注意,您还需要例如intptr_t / uintptr_t / ptrdiff_t / intmax_t / uintmax_t为非标准。在C ++中,这将运行相抵触转发进度保证的......不知道C.
TLW

0

IMO,一个很强的限制是(通过指针大小)可寻址空间是有限的,并且这是不可恢复的。

人们可以主张可以将内存“交换到磁盘”,但是在某些时候,地址信息本身将超过可寻址的大小。


这不是公认答案的重点吗?我认为这不会给2016年这个问题的答案增加任何新的内容。

@chi:不,可接受的答案没有提及交换到外部存储器,这可能被认为是一种解决方法。
Yves Daoust

-1

实际上,这些限制与图灵完整性无关。真正的要求是允许磁带任意长,而不是无限长。那会产生另一种停顿的问题(宇宙如何“计算”磁带?)

这就像在说“ Python并没有图灵完整,因为您无法将列表无限大”一样。

[编辑:感谢惠特里奇先生澄清了如何编辑。]


7
我不认为这可以回答问题。这个问题已经预料到了这个问题,并解释了为什么它无效:“尽管C标准允许size_t任意大,但必须将其固定为一定的长度,无论将其固定为多长,仍然是有限的”。您对此说法有何回应?我认为除非答案能解释为什么该论点是错误的(或正确的),否则我们不能将问题视为已回答。
DW

5
在任何给定时间,type的值size_t都是有限的。问题是您无法为size_t整个计算中的有效建立界限:对于任何界限,程序可能会溢出它。但是C语言指出存在一个界限size_t:在给定的实现中,它只能增长到sizeof(size_t)字节。另外,还不错。说批评您的人“不能独立思考”是不礼貌的。
吉尔(Gilles)'所以

1
这是正确的答案。车床不需要无限的胶带,它需要“任意长”的胶带。也就是说,您可以假定磁带的长度与完成计算所需的时间相同。您可能还假定计算机具有所需的内存。绝对不需要无限的磁带,因为在有限时间内停止的任何计算都不可能使用无限量的磁带。
Jeffrey L Whitledge,

这个答案表明,对于每个TM,您都可以编写一个具有足够指针长度的C实现来模拟它。但是,不可能编写一个可以模拟任何 TM的C实现。因此,规范禁止任何特定的实现都是T-complete。它本身也不是T-complete,因为指针长度是固定的。

1
这是另一个正确答案,由于该社区中大多数人的无能,因此很难看到。同时,被接受的答案是错误的,其评论部分由主持人删除关键评论来保护。再见,cs.stackexchange。
xamid

-1

可移动媒体使我们能够规避无限内存问题。也许人们会认为这是一种滥用,但我认为这是可以的,而且无论如何基本上是不可避免的。

修复通用图灵机的所有实现。对于磁带,我们使用可移动介质。当磁头超出当前光盘的末尾或开头时,机器会提示用户插入下一张或上一张。我们可以使用特殊的标记来表示模拟磁带的左端,也可以使磁带在两个方向上都是无界的。

这里的关键是C程序必须做的所有事情都是有限的。计算机仅需要足够的内存来模拟自动机,并且size_t只需要足够大以允许寻址(实际上相当小)数量的内存和光盘上的内存即可,该内存可以是任何固定的有限大小。由于仅提示用户插入下一张或上一张光盘,因此我们不需要无限制的大整数来表示“请插入光盘编号123456 ...”。

我想主要的反对意见可能是用户的参与,但这在任何实现中似乎都是不可避免的,因为似乎没有其他实现无限内存的方法。


3
我认为,除非C定义要求这种无限制的外部存储,否则不能将其视为Turing完整性的证明。(当然,ISO 9899并不要求是为实际工程编写的。)让我担心的是,如果我们接受这一点,则由于类似的原因,我们可能会声称DFA已完成Turing,因为可以使用它们在磁带(外部存储设备)上驱动磁头。

@chi我看不到DFA参数如何遵循。DFA的全部要点在于,它仅具有对存储的读取权限。如果允许它“在磁带上驱动磁头”,那不是图灵机吗?
David Richerby

2
确实,我在这里有点挑剔。关键是:为什么不能在C中添加“磁带”,让C模拟DFA,并利用这一事实声称C是Turing完成,而我们却不能对DFA这样做呢?如果C没有办法自己实现无限内存,则不应认为C是图灵完成的。(至少我会称其为“道德上的”图灵图,至少是因为界限如此之大,以至于在大多数情况下它们在实际中并不重要),我认为,要明确解决此事,就需要一个严格的正式规范C(ISO标准是不够的)

1
@chi可以,因为C包含文件I / O例程。DFA不会。
David Richerby

1
C并没有完全指定这些例程的功能-它们的大部分语义是实现定义的。不需要AC实现来存储文件内容,例如,我认为它的行为就好像每个文件都是“ / dev / null”一样。也不需要存储无限量的数据。我会说,当考虑到绝大多数C实现会做什么并将该行为概括为理想的机器时,您的观点是正确的。如果仅严格依赖C定义,而忘记实践,我认为它不成立。

-2

选择size_t无限大

您可以选择size_t无限大。自然地,不可能实现这样的实现。鉴于我们所生活的世界有限,这不足为奇。

实际影响

但是,即使有可能实现这种实现,也将存在实际问题。考虑以下C语句:

printf("%zu\n",SIZE_MAX);

SIZE_MAXSIZE_MAXO(2size_t)size_tSIZE_MAXprintf

幸运的是,出于我们的理论目的,我在规范中找不到任何保证printf所有输入都将终止的要求。因此,据我所知,我们在这里没有违反C规范。

论计算完整性

仍然有证据证明我们的理论实现是图灵完成。我们可以通过实现“任何单头图灵机”来证明这一点。

我们大多数人可能已经将Turing Machine实施为学校项目。我将不提供特定实现的详细信息,但这是一种常用的策略:

  • 状态数,符号数和状态转换表对于任何给定的机器都是固定的。因此,我们可以将状态和符号表示为数字,并将状态转换表表示为二维数组。
  • 磁带可以表示为链表。我们可以使用一个双链表,也可以使用两个单链表(从当前位置到每个方向一个链表)。

现在,让我们看看实现这样的实现需要什么:

  • 表示某些固定但任意大的一组数字的能力。为了表示任意数字,我们选择MAX_INT也为无穷大。(或者,我们可以使用其他对象来表示状态和符号。)
  • 为我们的磁带构造一个任意大的链表的能力。再次,大小没有限制。这意味着我们无法预先构建此列表,因为我们将永远花费只是构建磁带。但是,如果使用动态内存分配,则可以逐步构造此列表。我们可以使用malloc,但还必须考虑以下几点:
    • malloc如果可用内存不足,C规范将失败。因此,我们的实现只有在malloc永不失败的情况下才是真正通用的。
    • 但是,如果我们的实现是在具有无限内存的机器上运行的,那么就没有必要malloc失败。在不违反C标准的情况下,我们的实现将保证它malloc永远不会失败。
  • 取消引用指针,查找数组元素以及访问链接列表节点的成员的能力。

因此,上面的列表是在我们假设的C实现中实现Turing Machine所必需的。这些功能必须终止。但是,除标准另有要求外,其他任何事情都可以允许不终止。这包括算术,IO等。


6
printf("%zu\n",SIZE_MAX);这样的实现会打印出什么?
Ruslan

1
@Ruslan,这样的实现是不可能的,就像不可能实现图灵机一样。但是,如果可以实现这种实现,我想它将打印出一个无限大数字的十进制表示形式-大概是无限的十进制数字流。
内森·戴维斯

2
@NathanDavis可以实现图灵机。诀窍在于您不需要构建无限的磁带,而只是根据需要逐步构建磁带的已用部分。
吉尔(Gilles)'所以

2
@吉尔斯:在我们生活的这个有限的宇宙中,不可能实现图灵机。
gnasher729

1
@NathanDavis但是,如果执行此操作,则表示已更改sizeof(size_t)(或CHAR_BITS)。您无法从新状态恢复,必须重新启动,但是由于这些常量不同,因此程序的执行可能会有所不同
Gilles'SO- stop not evil'16

-2

这里的主要论点是size_t的大小是有限的,尽管可以无限大。

有一个解决方法,尽管我不确定这是否与ISO C一致。

假设您有一台内存无限的机器。因此,您不受指针大小的限制。您仍然有您的size_t类型。如果您问我什么是sizeof(size_t),答案将只是sizeof(size_t)。例如,如果您询问是否大于100,则答案为是。如果您问什么是sizeof(size_t)/ 2,您可能会猜答案仍然是sizeof(size_t)。如果您要打印它,我们可以就某些输出达成一致。两者的区别可以是NaN等。

总结是,放宽size_t具有有限大小的条件不会破坏任何现有程序。

PS仍可以分配内存sizeof(size_t),您只需要可数的大小,因此,假设您采用了所有偶数(或类似的技巧)。


1
“这两者的区别可以是NaN”。不,它不可能。C中没有整数类型的
NaN。– TLW

根据标准,sizeof必须返回size_t。因此,您必须选择一些特定的值。
Draconis

-4

是的。

1.引用答案

作为对我(和其他)正确答案的大量否决的回应(与令人震惊的错误答案认可相比),我寻求了一种理论上不太深刻的替代解释。我找到了这个。我希望它能涵盖一些此处常见的谬论,以便能提供更多的见解。参数的基本部分:

[...]他的论据如下:假设一个人编写了一个给定的终止程序,在执行该程序时可能需要多达任意数量的存储空间。在不更改该程序的情况下,有可能实现一个计算机硬件及其C编译器的后验,后者提供足够的存储空间来容纳此计算。这可能需要加宽char(通过CHAR_BITS)和/或指针(通过size_t)的宽度,但是无需修改程序。因为这是可能的,所以C确实是终止程序的图灵完成。

这个论点的棘手部分是它仅在考虑终止程序时才起作用。终止程序具有很好的属性,即它们具有静态的存储需求上限,可以通过在存储容量增大的情况下在所需输入上运行该程序来实验确定该上限,直到“适合”为止。

我之所以会误导我,是因为我正在考虑更多种类的“有用的”非终止程序[...]

简而言之,由于每个可计算函数都有C语言的解决方案(由于上限不受限制),所以每个可计算问题都有一个C程序,因此C是图灵完备的。

2.我的原始答案

理论计算机科学中的数学概念(例如图灵完备性)与其实际应用(即实用计算机科学中的技术)之间存在广泛的混淆。图灵完整性不是物理上现有机器的属性,也不是时空受限模型中的任何属性。它只是描述数学理论性质的抽象对象。

就像几乎任何其他常见的编程语言一样,C99不受图文库的限制,不受基于实现的限制,因为它能够表达功能上完整的一组逻辑连接词,并且原则上可以访问无限量的内存。人们指出,C明确限制了对随机存储器访问的限制,但这是无可避免的,因为这些都是C标准中另外规定的限制,而图灵完备性却需要它们

关于逻辑系统,这是一件非常基本的事情,应该足以满足非构造性证明。考虑具有一些公理图式和规则的演算,这样逻辑结果集为X。现在,如果添加一些规则或公理,则逻辑结果集会增长,即必须是X的超集。这就是为什么这样的原因,模态逻辑S4正确包含在S5中。同样,当您有一个图灵完备的子规格,但在顶部添加了一些限制时,这些限制不会阻止X中的任何后果,即,必须有一种方法可以规避所有限制。如果要使用非图灵完备的语言,则必须减少而不是扩展演算。声称不可能实现但实际上确实可行的扩展只会增加不一致之处。C标准中的这些不一致可能不会带来任何实际后果,就像Turing完整性与实际应用无关。

基于递归深度模拟任意数(即 ;具有支持经由调度/伪线程多个号码的可能性; 没有理论限制在C递归深度),或使用文件存储到模拟无限程序存储器(想法)是建设性地证明C99的图灵完备性的无限可能中,只有两种。人们应该记得,对于可计算性而言,时间和空间的复杂性是无关紧要的。尤其是,假设一个有限的环境来伪造图灵完备性仅仅是循环推理,因为该限制排除了所有超出预设复杂性界限的问题。

注意:我写这个答案只是为了防止人们因某种面向应用的有限思维而停滞不前以获得数学直觉。很遗憾的是,大多数学习者都会因为接受错误的答案而阅读错误的答案。推理的根本缺陷,因此更多的人会散布这种错误的信念。如果您对这个答案不满意,那只是问题的一部分。)


4
我不关注你的最后一段。您声称增加限制会增加表达能力,但这显然是不正确的。限制只能降低表达能力。例如,如果您使用C并添加限制,即程序不能访问超过640kb(任何类型)的存储,那么您已将其变成了花哨的有限自动机,显然它不是图灵完备的。
David Richerby

3
如果您拥有固定的存储量,则无法模拟需要更多资源的任何事物。您的内存可能只有有限的许多配置,这意味着您只能做有限的许多事情。
David Richerby

2
我不明白您为什么提到“物理上存在的机器”。请注意,图灵完备性是数学计算模型的属性,而不是物理系统的属性。我会同意,作为有限对象的任何物理系统都无法接近图灵机的强大功能,但这无关紧要。我们仍然可以采用任何编程语言,考虑其语义的数学定义,并检查该数学对象是否是图灵完整的。即使没有可能的物理实现,Conway的生活游戏也具有图灵强大的功能。

2
@xamid如果您对本网站的审核政策有疑问,请将其转到Computer Science Meta。在此之前,请保持友善。不能容忍对他人的语言虐待。(我已删除了与当前主题无关的所有评论。)
拉斐尔

2
您说修改指针的宽度不会更改程序,但是程序可以读取指针的宽度并使用该值执行任何所需的操作。相同CHAR_BITS
Draconis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.