23

Coders at Work (p355) 一书中,Guy Steele 谈到了 C++:

我认为与 C 向后兼容的决定是一个致命的缺陷。这只是一系列无法​​克服的困难。C 基本上有一个损坏的类型系统。它足以帮助您避免一些困难,但它不是密封的,您不能指望它

他将类型系统描述为“腐败”是什么意思?

你能用 C 语言的一个简单例子来演示吗?

编辑

  1. 这句话听起来很有争议,但我不想成为。我只是想明白他的意思。

  2. 请用C而不是C++给出例子。我也对“基本”部分感兴趣:)

4

7 回答 7

19

C 中非类型安全的明显示例仅来自于您可以从 void * 转换为任何类型而无需显式转换的事实。

struct X
{
  int x;
};

struct Y
{
  double y;
};

struct X xx;
xx.x = 1;
void * vv = &xx;
struct Y * yy = vv; /* no need to cast explicitly */
printf( "%f", yy->y );

当然 printf 本身并不是完全类型安全的。

C++ 不是完全类型安全的。

struct Base
{
   int b;
};

struct Derived : Base
{
  int d;

  Derived() 
  {
     b = 1;
     d = 3;
  }
};

Derived derivs[50];
Base * bb = &derivs[0];
std::cout << bb[3].b << std::endl;

将 Derived* 转换为 Base* 没有问题,但是当您尝试将 Base* 用作数组时会遇到问题,因为它会使指针算法全部错误,而所有 b 值都是 1,您很可能会得到 3 (因为整数将变为 1-3-1-3 等)

于 2010-11-08T14:49:59.643 回答
4
char buffer[42];
FunctionThatDestroysTheStack(buffer);  // By writing 43 chars or more
于 2010-11-08T14:43:47.457 回答
4

基本上你可以将任何数据类型转换为任何数据类型

struct SomeStruct {
    void* data;
};

struct SomeStruct object;
*( (int*) &object ) = 10;

没有人抓住你。

于 2010-11-08T14:45:21.873 回答
3

C 类型系统确实存在一些问题。诸如隐式函数声明和隐式转换之类的东西void*可以静默地破坏类型安全。

C++ 修复了几乎所有这些漏洞。C++ 类型系统不向后兼容 C,它只兼容编写良好的类型安全 C 代码。

此外,反对 C++ 的人通常会将您指向 Java 或 C# 作为“解决方案”。然而,Java 和 C# 在它们的类型系统中确实存在漏洞(数组协方差)。C++ 没有这个问题。

编辑:示例,在 C++ 中,尝试使用 Java 和 C# 类型系统(不正确地)允许的数组协方差。

#include <stdlib.h>

struct Base {};
struct Derived : Base {};

template<size_t N>
void func1( Base (&array)[N] );

void func2( Base** pArray );

void func3( Base*& refArray );

void test1( void )
{
  Base b[40];
  Derived d[40];

  func1(b); // ok
  func1(d); // error caught by C++ type system
}

void test2( void )
{
  Base* b[40] = {};
  Derived* d[40] = {};

  func2(b); // ok
  func2(d); // error caught by C++ type system

  func3(b[0]); // ok
  func3(d[0]); // error caught by C++ type system
}

结果:

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 19: error: no instance of function template "func1" matches
          the argument list
            The argument types that you used are: (Derived [40])
        func1(d); // error caught by C++ type system
        ^

"ComeauTest.c", line 28: error: argument of type "Derived **" is incompatible with
          parameter of type "Base **"
        func2(d); // error caught by C++ type system
              ^

"ComeauTest.c", line 31: error: a reference of type "Base *&" (not const-qualified)
          cannot be initialized with a value of type "Derived *"
        func3(d[0]); // error caught by C++ type system
              ^

3 errors detected in the compilation of "ComeauTest.c".

这并不意味着 C++ 类型系统中根本没有漏洞,但它确实表明您不能像 Java 和 C# 允许的那样用指向 Base 的指针静默覆盖指向 Derived 的指针。

于 2010-11-08T14:51:09.840 回答
2

你必须问他什么意思才能得到一个明确的答案,或者为这句话提供更多的背景。

然而,很明显,如果这是 C++ 的一个致命缺陷,那么这种疾病是慢性的,而不是急性的——C++ 正在蓬勃发展,并且不断发展,正如持续的 Boost 和 C++0x 努力所证明的那样。

我什至不再认为 C 和 C++ 是耦合的——在各自的论坛上呆了几周,很快就解决了他们是两种不同的语言这一事实​​的困惑之一,每种语言都有自己的优势和弱点。

于 2010-11-08T14:45:28.363 回答
2

恕我直言,C 类型系统中“最破碎”的部分是

  • 可选的值/参数
  • 可变值/按引用传递
  • 数组
  • 非 POD 函数参数

都映射到单一语言概念“指针”。这意味着,如果你得到一个类型的函数参数X*,它可能是一个可选参数,它可能会改变函数指向的值,可能是在指向的X*那个之后有多个实例X(它是开放的多少 - 数字可以作为单独的参数传递,或者某种特殊的“终止符”值可能会标记数组的结尾,如以 nul 结尾的字符串)。或者,参数可能只是一个单一的结构,你不希望改变它,但通过引用传递它更便宜。

如果你得到 type 的东西X**,它可能是一个可选值数组,或者它可能是一个简单值数组,你应该改变它。或者它可能是一个二维锯齿状数组。或通过引用传递的可选值。

相比之下,以 ML 语言家族(F#、OCaML、SML)为例。在这里,这些概念映射到不同的语言结构:

  • 可选的值具有类型X option
  • 可变/通过引用传递的值具有类型X ref
  • 数组具有类型X array
  • 和非 POD 类型可以像 POD 一样传递。因为它们不是可变的,编译器可以在内部通过引用传递它们,但你不需要知道那个实现细节

您当然可以将它们组合起来,int optional ref即可变值,可以设置为空值或某个整数值。int ref optional另一方面是可选的可变值;它可以什么都没有(没有人可以改变它)或者它可以是一些可变的 int (你可以将它改变为任何其他可变的它,但不能改变它)。

这些区别非常微妙,但是无论您是否使用 ML 编程,您都必须做出这些区别。在 C 语言中,您必须做出相同的区分,但它们并没有在类型系统中明确说明。您必须非常仔细地阅读文档,否则如果您误解了何时使用哪种指针,您可能会引入 sublte(阅读:难以找到)错误。

于 2010-11-08T16:22:03.767 回答
1

在这里,“损坏”意味着它不是“严格”的,导致 C++ 中永无止境的乐趣(由于许多自定义类型(对象)和重载运算符,强制转换成为 C++ 中的一大麻烦)。

对 C 的攻击涉及其错位使用作为严格的 OOP 基础。

C 从未被设计为限制编码人员,因此可能是学术界的挫败感(以及 BS 给予世界的 ++ 的华丽辉煌)。

“我发明了面向对象这个术语,我可以告诉你我并没有想到 C++”

(艾伦·凯)

于 2010-11-08T17:49:26.000 回答