C ++或C99理论上可以编译成同等便携的C90吗?

时间:2011-03-18 08:53:55

标签: c++ c compiler-construction c-preprocessor

这是一个很大的问题,所以让我先解决一些问题:

  1. 让我们忽略一些事实,即某些C ++特性无法在C中实现(例如,支持链接到的任何全局静态对象的预主要初始化)。
  2. 这是一个关于理论上可行的思想实验。请不要写这么说(我知道)有多难,或者我应该做X。这不是一个实际的问题,它是一个有趣的理论问题。 :)
  3. 问题是:理论上是否可以将C ++或C99编译为作为原始源代码的便携式

    Cfront和Comeau C / C ++已经将C ++编译为C语言。但根据Comeau的销售人员的说法,对于Comeau来说,他们生产的C不是便携式的。我自己没有使用过Comeau编译器,但我推测其原因是:

    1. 已经扩展了诸如INT_MAX,offsetof()等宏,并且它们的扩展是特定于平台的。
    2. #ifdef等条件编辑已经解决。
    3. 我的问题是这些问题是否可以以强有力的方式克服。换句话说,是否可以编写完美的 C ++到C编译器(以不可支持的C ++特性为模)?

      诀窍在于你必须扩展宏以进行强大的解析,然后将它们折叠回未扩展的形式(因此它们再次是可移植的和与平台无关的)。但是,是否存在根本不可能的情况?

      任何人都很难断然说“是的,这是可能的”,但我很有兴趣看到任何具体的反例:由于某些深层原因无法以这种方式编译的代码片段。我对C ++和C99反例感兴趣。

      我将从一个粗略的例子开始,只是为了说明我认为反例的样子。

      #ifdef __SSE__
      #define OP <
      #else
      #define OP >
      #endif
      
      class Foo {
       public:
        bool operator <(const Foo& other) { return true; }
        bool operator >(const Foo& other) { return false; }
      };
      
      bool f() { return Foo() OP Foo(); }
      

      这很棘手,因为OP的值以及此处生成的方法调用是特定于平台的。但似乎编译器可能会认识到语句的解析树依赖于宏的值,并将宏的可能性扩展为:

      bool f() {
      #if __SSE__
         return Foo_operator_lessthan(...);
      #else
         return Foo_operator_greaterthan(...);
      #endif
      }
      

6 个答案:

答案 0 :(得分:2)

这不仅在理论上是可行的,而且实际上也是微不足道的 - 使用cbe目标的LLVM。

答案 1 :(得分:1)

理论上,所有图灵完备语言都是等价的。

您可以将C ++编译为目标代码,然后将其反编译为纯C或使用以纯C编写的解释器。

答案 2 :(得分:1)

理论上当然,任何事情都可以先编译成C语言,但这样做是不切实际的,特别是对C ++而言。

对于Foo运算符&lt;在您的示例中,它可以转换为:

bool isLess(const struct Foo * left, const struct Foo * right );

作为功能签名。 (如果C90不允许bool然后返回int或char,同样旧的C版本不允许使用const,只是不要使用它。)

虚函数更棘手,需要函数指针。

struct A
{
   virtual int method( const std::string & str );
};

struct A
{
   int (*method)( struct A*, const struct string *);
};

a.method( "Hello" );


a.method( &a, create_String( "hello" ) ); 
          // and take care of the pointer returned by create_String

答案 3 :(得分:1)

存在许多细微差别。例如,请考虑以下行:

int i = UINT_MAX;

IIRC,在C ++中,它指定了一个实现定义的值。在C99和C89中,它分配实现定义的值,或者引发实现定义的信号。因此,如果你在C ++中看到这一行,你不能只是将它修改为未经修改的C89编译器,除非你做出不可移植的假设它不会发出信号。

不过,如果我记得错了,请考虑一下你自己与相对简单的表达方式相关的标准差异的例子......

因此,正如“grep”所说,你可以这样做,因为C89是一种足够丰富的语言来表达一般计算。基于同样的原因,您可以编写一个发出Perl源代码的C ++编译器。

根据你的问题的声音,你想象编译器会对原始代码进行一组定义的修改,使其编译为C89。实际上,即使对于C ++或C99中的简单表达式,发出的C89也可能看起来与原始源不同。

另外,我忽略了可能是你无法实现的标准库的某些部分,因为C89不提供这些功能,所以你最终会得到一个“编译器”,但不是一个完整的实现。我不确定。正如dribeas指出的那样,低级函数(如VLA)存在问题 - 基本上你无法将C89“堆栈”作为C99“堆栈”使用。相反,您必须从C89动态分配内存以用于C99源中所需的自动变量。

答案 4 :(得分:1)

一个大问题是例外。可以使用setjmplongjmp等来模拟它们,但与真正的设备感知展开引擎相比,这总是非常低效。

答案 5 :(得分:0)

http://www.comeaucomputing.com

没有比工作实例更好的可行性证明。 Comeau是最符合标准的c ++ 03编译器之一,它支持即将推出的标准的许多功能,但它并不真正生成二进制代码。它只是将您的c ++代码转换为可以使用不同的C后端编译的c代码。

至于便携性,我认为这是不可能的。如果没有特定于编译器的扩展,有些功能无法实现。我想到的第一个例子是C99动态数组:int n; int array[n];无法在纯C89(AFAIK)中实现,但可以在alloca等扩展程序之上实现。