我正在阅读有关C++向量的文章,其中提到C语言中的memcpy
和printf
函数不具备类型安全性。文章链接: http://en.wikipedia.org/wiki/Vector_(C%2B%2B)。
问题: 简单来说,什么是类型安全,有哪些“类型安全”的替代方案?
我正在阅读有关C++向量的文章,其中提到C语言中的memcpy
和printf
函数不具备类型安全性。文章链接: http://en.wikipedia.org/wiki/Vector_(C%2B%2B)。
问题: 简单来说,什么是类型安全,有哪些“类型安全”的替代方案?
类型安全意味着编译器可以检查您是否使用了正确的类型。例如,如果您使用 printf
,您可能会因编写以下内容而意外崩溃程序:
printf("The meaning of life is %s", 42);
因为42是一个整数,而不是一个字符串。
类型安全(Type safety) 是指编译器会帮助检查你是否混淆了(不兼容的)数据类型。
例如,当你调用 memcpy
函数时,函数(和编译器)只看到内存中的两个指针,并会愉快地开始复制数据。这意味着你可以像这样混合使用不兼容的数据类型:
SomeClass a;
AnotherClass b;
memcpy((void*)&a, (void*)&b, sizeof(b));
获得类型安全有很多方法。可以使用模板并在mempcy()周围创建包装器,确保两个指针指向相同的数据类型,或者可以使用其他方式。
由于您已经在使用STL的向量,因此您已经在使用更多或更少类型安全的实现。
类型安全控制编译器检查变量是否是正确的类型。C语言在数据类型方面非常宽松,例如,ANSI C标准实际上规定了类型提升会发生在char
数据类型中。下面的示例将解释这一点。
char ch = 32; /* that is a space character accordingly to ASCII */
int n = ch + 3;
ch
变量“提升”为int
类型。这是合法的,但如果您暗示这是您想要的结果,则需要更仔细地检查。int n = (int)3.1415926535f;
不在意细节的话,那就是一个π值,发生的情况是变量n
的值将为3。
上面说明了类型安全以及C语言在这方面非常宽松。
现代语言中,类型安全更加严格,如Java、C#,目的是限制变量的使用和含义。PHP是松散类型的一个很好的例子,你可以这样做:
$myvar = 34;
$myvar = $myvar + "foo";
$myvar
是整数、浮点数还是字符串?在这里类型安全性不够清晰,可能会导致错误和繁琐的调试过程。
希望这可以帮到你。
既然您已经在维基百科上了:类型安全。
类型安全大致意味着,语言会防止您意外混淆类型。
memcpy
不是类型安全的,因为您可以轻松地将某个int
的内存复制到一个char
数组中,并最终得到无意义的数据。 printf
也不是类型安全的,因为您可以为字符串提供%i
格式说明符;同样,该字符串将被解释为一个int
,并最终得到垃圾值。(顺便说一下,VC++编译器在某些情况下确实会检查格式字符串。)
std::vector<T>
是类型安全的,因为它只允许您将给定类型T
的值放入其中。 (当然,您可以进行显式类型转换,但重点是您必须明确地执行不安全的操作)。
对于数组和容器,类型安全的替代memcpy函数可以使用<algorithm>
中的std::copy
- 如果所有涉及的类型都满足某些要求,则可以基于memmove实现,否则它执行赋值操作 - 对于某些类,如果您绕过它们的公共接口并在内存中移动/复制它们(例如,我认为任何具有非平凡复制构造函数的类都会出现问题),则可能会破坏某些不变量。
C I/O例程的类型安全替代是iostreams(如果您想要格式字符串的好处,则可以使用boost::format
)。
这意味着如果你试图以不符合该类型意义的方式使用类型,编译器将不会生成警告。例如,以下是未定义行为,在实践中将把指针的位复制到浮点数的位中,其中它们根本没有意义。如果 sizeof(char*)
> sizeof(float)
,它将覆盖刚好在 f
存储位置上方的任何内存位置。
float f;
char *c = someString();
memcpy(&f, &c, sizeof(char*));
c
;如果稍后使用,可能会在 f
中生成陷阱表示,这将是 U.B;可能会溢出缓冲区,就像您所发现的那样。 - Steve Jessop类型安全是指在编译时强制每个变量都有一个专用类型的编码范例,例如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期望第二个参数为字符串,但得到的是一个浮点数。
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
不是类型安全的原因。它不检查任何内容,只是从一个指针复制到另一个指针。