【问题标题】:What is type safety and what are the "type safe" alternatives? [duplicate]什么是类型安全以及“类型安全”的替代方案是什么? [复制]
【发布时间】:2011-01-09 13:38:10
【问题描述】:

可能的重复:
What is Type-safe?
What is type-safety?

我正在阅读有关 C++ 向量的内容,有人提到 C 中的 memcpyprintf 函数不是类型安全的。文章在这里:http://en.wikipedia.org/wiki/Vector_(C%2B%2B)

问题:用简单的英语来说,什么是类型安全以及“类型安全”的替代方案是什么?

【问题讨论】:

标签: c++ c type-safety


【解决方案1】:

类型安全意味着编译器可以检查您是否使用了正确的类型。例如,如果您使用printf,您可能会通过编写以下代码意外地使程序崩溃:

printf("The meaning of life is %s", 42);

因为 42 是整数,而不是字符串。

【讨论】:

  • 正确; C++ 是一个弱类型系统,因为您基本上可以将任何类型转换为任何其他类型,将 int 转换为 bool 和其他所有类型。 C++ 赋予程序员对机器的完全控制权;内存就是内存,而 C++ 会让你大吃一惊,因为它要求你准确地知道每一步都在做什么。
【解决方案2】:

Type safety 表示编译器将帮助检查您没有混合(不兼容)数据类型。

例如,当您调用memcpy 时,函数(和编译器)只在内存中看到两个指针,并且会愉快地开始复制数据。这意味着您可以像这样混合不兼容的数据类型:

SomeClass a;
AnotherClass b;
memcpy((void*)&a, (void*)&b, sizeof(b));

获得类型安全的方法有很多。您可以使用模板并围绕 mempcy() 进行包装,确保两个指针指向相同的数据类型,或者您可以使用其他方式。

由于您已经在使用来自 STL 的向量,因此您已经在使用或多或少的类型安全实现。

【讨论】:

    【解决方案3】:

    类型安全控制编译器检查变量是否为正确类型的使用。 C 在数据类型安全方面非常松散,例如,这实际上是在 ANSI C 标准中,声明数据类型 char 将发生类型提升,本作业中的示例将对此进行说明,

    char ch = 32; /* that is a space character accordingly to ASCII */
    int n = ch + 3;
    

    注意ch 变量如何被“提升”为类型int。这是合法的,但如果这是您的暗示,则需要仔细检查。

    诸如 C# 编译器之类的编译器不会允许这种情况发生,这就是为什么在 C 中使用 cast 运算符的原因:

    int n = (int)3.1415926535f;
    

    不挑剔,这是一个 pi 值,发生的情况是 n 的值将是 3。

    以上用于说明类型安全性,C 在这方面非常宽松。

    现代语言中的类型安全更加严格,例如Java、C#,以限制变量的使用和含义。 PHP 是松散类型的一个很好的例子,你可以这样做:

    $myvar = 34;
    $myvar = $myvar + "foo";
    

    $myvar 一个整数,或者它是一个浮点数还是一个字符串。这里的类型安全对于可能导致错误的意图以及试图弄清楚发生了什么的愉快的调试会话并不是很清楚。

    希望对你有帮助

    【讨论】:

      【解决方案4】:

      因为你还是在维基百科上:Type safety

      类型安全,粗略地说,是指语言禁止你不小心混淆你的类型。

      memcpy 不是类型安全的,因为您可以轻松地将一些int 的内存复制到char 数组中,并最终得到无意义的数据。 printf 不是类型安全的,因为您可以提供带有字符串的 %i 格式说明符;再一次,字符串将被解释为int,你最终会得到垃圾。 (顺便说一句,VC++ 编译器确实在某些情况下检查格式字符串。)

      std::vector<T> 是类型安全的,因为它只允许您将给定类型 T 的值放入其中。 (当然,您可以进行显式类型转换,但关键是您必须显式 做一些不安全的事情)。

      【讨论】:

      • 我想知道为什么这被否决了。我说错了吗?
      • 这是你的投票,伙计 ;)
      【解决方案5】:

      “类型安全”意味着编译器会检查您是否使用正确的类型做正确的事情(例如,如果您尝试将 Banana 视为橙色,或将字符串提供给期望输出的函数,则会触发编译器错误一个整数)。

      void* 出现时,类型安全(大多数情况下)就消失了——它是一个可以指向任何东西的指针(完全不知道所涉及的类型),并且语言完全离开了它在程序员手中(例如,void* 除了被强制转换回原始类型之外,对任何事情都没有什么好处;它可以代表任何东西,但在使用它之前你必须知道它是什么)。

      类型不安全也与 printf 等可变参数函数一起使用(编译器不关心有多少参数以及它们的类型 - 再次由调用者确保格式字符串与参数匹配并且它们的类型)。

      memcpy 的类型安全替代方案(用于数组和容器)可以是 std::copy in <algorithm> - 如果所有涉及的类型都满足某些要求,它可以根据 memmove 来实现,否则它会执行分配 - 对于某些类你如果您绕过它们的公共接口并在内存中移动/复制它们,则可能会破坏某些不变量(例如,如果您使用 memcpy 复制它,我认为任何具有非平凡复制构造函数的类都会出现异常行为)。

      C I/O 例程的类型安全替代方案是 iostream(如果您想要格式字符串的好处,boost::format)。

      【讨论】:

        【解决方案6】:

        “类型安全”使用“类型系统”来确保错误不会在程序中传播。例如,在没有类型安全的情况下,可能会以某种不合需要的方式(默默地)将字符串类型添加到浮点类型。

        在您谈论的实例中,memcpy()printf(),缺乏类型安全是由于函数如何处理它们的参数。例如,使用 memcpy(arg1, arg2, len),从内存地址 arg2 开始的 len 字节将被复制到内存地址 arg1,无论 arg1 指向多少字节,都可能覆盖程序的其他部分。

        对于类型安全的替代方案,请查看 constructorscout.

        其实look into the entire C++ FAQ Lite

        【讨论】:

          【解决方案7】:

          这意味着如果您尝试以对该类型没有意义的方式使用类型,编译器将不会生成警告。例如,以下是未定义的行为,实际上会将指针的位复制到浮点数的位中,在那里它们绝对没有意义。如果sizeof(char*) > sizeof(float),它将覆盖恰好位于f 所在位置上方的任何内存位置。

          float f;
          char *c = someString();
          memcpy(&f, &c, sizeof(char*));
          

          【讨论】:

          • 实际上这是未定义的行为,原因有很多:使用未初始化的值c;可能会在 f 中生成一个陷阱表示,如果以后使用它将是 U.B;如您所见,可能会超出缓冲区。
          • @Steve Jessop:未初始化的值 c 是一个错误,不是故意的。我已经修复了它,因为它分散了真正的注意力。感谢您指出。
          【解决方案8】:

          memcpy 函数的签名是

          void *memcpy (void* destination, const void* source, size_t num);
          

          所以你可以看到它没有假设与复制有关的指针,它们只是指针。因此,例如,如果您想将ints 的范围复制到floats 的范围,编译器不会对此抱怨。

          类型安全 是一种工具,可帮助开发人员通过防止某种错误代码被编译(和最近执行)来避免某些错误。它分析源代码的语义方面,以检查类型和类型之间的转换是否一致。

          这是什么意思?这意味着如果您的程序通过了类型检查阶段,您可以确保不会在运行时产生CERTAIN KIND的错误。

          当然,有时您需要强制不执行此检查,这就是为什么您可以使用强制转换来强制执行您想要的操作。想想另一个例子,malloc:它被定义为

          void* malloc (size_t size);
          

          所以当你想分配一个指向 floats 的指针时,你可以这样做:

          float* ptr = (float*)malloc(sizeof(float*)*COUNT);
          

          您必须强制将函数的结果强制转换为 float* 否则类型检查会发现将 void* 分配给 float*void* 太通用而无法分配:类型检查失败!

          这就是为什么memcpy 不是类型安全的。它不检查任何东西,它只是从一个指针复制到另一个指针。

          【讨论】:

            【解决方案9】:

            类型安全是指强制每个变量在编译时具有专用类型的编码范例,例如int a = 4; double d = 100.0; struct ms {char s;} mystruct; 变量的类型永远不会“丢失”。如果要将其类型从 a 更改为 b,则必须定义显式或隐式转换。

            printf类型安全的,因为您在可变参数列表中传递参数:

            float f = 1.f;
            printf("This is a float: %f\nAnd this is a string: %s",f,f);
            

            printf 函数不知道它接收哪种类型的值。实现使用格式字符串来查找,但如果字符串错误,实现没有机会找到它,因为在编译时没有可用的类型信息。上面的printf 调用很可能以灾难告终——printf 期望一个字符串作为第二个参数,但得到一个浮点数。

            【讨论】:

            • 我只想补充一点,可以隐式或显式声明类型。 a = 3;显然 a 是一个 int。
            【解决方案10】:

            答案的简短版本:

            class Person;
            
            person.DoSomething(); // This is type safe.
            
            void * p = &person; // You can now start doing unsafe things with p.
            

            您不能将 Person 传递给 memcpy。它只知道和关心内存。字节。

            【讨论】:

            • 如果 Person 是 POD 类,您可以将(指向 a)Person 的指针传递给 memcpy。我认为与类型安全问题更相关的是,您不能(例如)将 Person 存储到不够大的目的地。 std::copy 是类型安全的,它要求目标类型可以从源类型中分配,而 memcpy 则没有。
            • @Steve Jessop:std::copy 也不进行任何范围检查(目标缓冲区可能太小)。 IMO,memcpy 的最大问题是您将一个类仅视为字节的集合(通过强制转换为void*),从而绕过了复制构造函数。尝试使用 memcpy 复制std::string(如果它似乎可以工作,则在调试器中运行)。 - 在模板代码中使用 memcpy 是完全不可能的,除非以某种方式确保您只将它与 POD 类型一起使用。
            猜你喜欢
            • 2010-09-20
            • 2014-07-24
            • 2011-01-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多