什么是类型安全,有哪些“类型安全”的替代方案?

31

2
我相信我们在Stack Overflow上已经有了你两个问题的答案,分别在不同的问题中。我会查找一下。同时,在Stack Overflow上搜索“类型安全”、“memset memcpy std::fill”。 - Mehrdad Afshari
投票关闭为精确重复:https://dev59.com/lnVC5IYBdhLWcg3wixs0 https://dev59.com/O0fSa4cB1Zd3GeqPAMsd https://dev59.com/33I-5IYBdhLWcg3wW24L - Sinan Ünür
9个回答

38

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

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

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


http://www.boost.org/doc/libs/1_45_0/libs/format/doc/format.html - Roman A. Taycher
9
正确;C++是一种弱类型系统,因为你基本上可以将任何类型转换为其他类型,比如将整数转换为布尔型或其他类型。C++让程序员完全控制机器; 内存就是内存,C++会让你自己炸腿,因为它要求你在每一步都非常明确地知道自己在做什么。 - Adam Miller

16

类型安全(Type safety) 是指编译器会帮助检查你是否混淆了(不兼容的)数据类型。

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

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

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

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


8

类型安全控制编译器检查变量是否是正确的类型。C语言在数据类型方面非常宽松,例如,ANSI C标准实际上规定了类型提升会发生在char数据类型中。下面的示例将解释这一点。

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

注意如何将ch变量“提升”为int类型。这是合法的,但如果您暗示这是您想要的结果,则需要更仔细地检查。
像C#编译器这样的编译器不允许发生这种情况,这就是为什么在C中使用强制转换运算符的原因。
int n = (int)3.1415926535f;

不在意细节的话,那就是一个π值,发生的情况是变量n的值将为3。

上面说明了类型安全以及C语言在这方面非常宽松。

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

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

$myvar是整数、浮点数还是字符串?在这里类型安全性不够清晰,可能会导致错误和繁琐的调试过程。

希望这可以帮到你。


7

既然您已经在维基百科上了:类型安全

类型安全大致意味着,语言会防止您意外混淆类型。

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

std::vector<T>是类型安全的,因为它只允许您将给定类型T的值放入其中。 (当然,您可以进行显式类型转换,但重点是您必须明确地执行不安全的操作)。


1
“类型安全”意味着编译器检查您是否正在使用正确的类型进行正确的操作(例如,如果您尝试将香蕉视为橙子或将字符串提供给期望输出整数的函数,则触发编译器错误)。
当涉及到“void*”时,“类型安全”(大多数情况下)就不再适用了——它是一个可以指向任何东西的指针(完全不知道所涉及的类型),语言完全交给程序员去处理(例如,“void*”除了被转换回原始类型外,基本上没有什么用处;它可以表示任何东西,但在使用之前必须知道它是什么)。
类型不安全也会在可变参数函数(如printf)中发挥作用(编译器不关心有多少参数以及它们的类型——再次由调用者确保格式字符串与参数及其类型匹配)。

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

C I/O例程的类型安全替代是iostreams(如果您想要格式字符串的好处,则可以使用boost::format)。


1
“类型安全”是使用“类型系统”来确保程序内部不会传播错误。例如,如果没有类型安全,可能会以某种不良方式(悄悄地)将字符串类型添加到浮点类型中。
在你所谈论的实例中,memcpy()printf()缺乏类型安全是由于这些函数如何处理它们的参数。例如,对于memcpy(arg1, arg2, len),从内存地址arg2开始的len字节将被复制到内存地址arg1,而不管arg1指向多少字节,可能会覆盖程序的其他部分。
要了解类型安全的替代方法,请查看构造函数cout.
实际上,请查看整个C++ FAQ Lite

1

这意味着如果你试图以不符合该类型意义的方式使用类型,编译器将不会生成警告。例如,以下是未定义行为,在实践中将把指针的位复制到浮点数的位中,其中它们根本没有意义。如果 sizeof(char*) > sizeof(float),它将覆盖刚好在 f 存储位置上方的任何内存位置。

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

实际上,这是未定义行为的原因有很多:使用未初始化的值 c;如果稍后使用,可能会在 f 中生成陷阱表示,这将是 U.B;可能会溢出缓冲区,就像您所发现的那样。 - Steve Jessop
@Steve Jessop: 未初始化的值 c 是一个错误,而非有意为之。我已经修复了它,因为它会分散注意力,让人无法专注于真正的重点。感谢你指出这个问题。 - dsimcha

0

类型安全是指在编译时强制每个变量都有一个专用类型的编码范例,例如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是一个整数。 - Lay González

0

memcpy 函数的签名是:

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

正如您所看到的,它不会假设与复制相关的指针,它们只是指针。因此,例如,如果您想将一系列ints复制到一系列floats,编译器不会对此抱怨。

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

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

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

void* malloc (size_t size);

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

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接