为什么strcpy到文字编译?

时间:2016-04-07 18:54:14

标签: c visual-studio strcpy

在Visual Studio 2015中,以下编译:

strcpy("destination", "Source");

编译器不应该知道"目的地"是文字,不能构成有效的非const char *参数?

作为旁注,它确实"正确"在它运行时崩溃。

1 个答案:

答案 0 :(得分:6)

上下文

字符串文字在C中一直是非const的。当前的标准草案n1570在6.4.5 / 6中说:

  

多字节字符序列[由相邻字符串文字的连接产生,-ps]然后用于初始化静态存储持续时间的数组,并且长度足以包含序列。对于字符串文字,数组元素具有   键入char [而不是const char,-ps]。

原因当然,原来他们 确实通常是可写的。该计划本身是可写的;甚至还有自我修改的代码。这是相关的,因为字符串文字是由编译器“与程序一起”生成和存储的。

现代内存管理 - 即高级机器架构的问题 - 这使得在访问程序的内存时可以生成硬件异常。使用这种可能性是一个安全问题。并非所有体系结构(都可以)都这样做,即使在今天,编译器也可以选择控制字符串的位置(例如-fwritable-strings使用旧的gcc)。

本守则

在语法上,代码是兼容的,在语义上它是n1570中每6.4.5 / 7的UB:“如果程序试图修改这样的数组,行为是 未定义。“

当字符串文字的地址被分配给非常量变量时​​(或者用于在函数调用中初始化非常量参数),编译器可以发出警告,但是我尝试的常见变量并没有警告哪些让我感到困惑 - a许多已实施的警告似乎不那么重要和吵闹。

的strcpy()

关于strcpy()的细节:有些评论说“编译器不知道strcpy()做了什么”。这往往是误导性的:

  • 标准库函数由标准定义。这些知识可以在编译器中使用。例如,像lint这样的工具通常知道这种语义。
  • 编译器和默认标准库通常是密切合作开发的,并且是捆绑在一起的;因为在编译库本身,编译器引导等方面,编译器和库之间存在大量的交互。通常在两个项目之间定期进行交换。
  • 编译器可以自由地用内在函数替换库函数,这将给予他们非常熟悉的知识。

GCC

确实,gcc happens to replace strcpyand many other functions with built-ins,所以它确实有第一手信息,第一个地址将被写入。它只是不使用它。

另一个gcc内在函数是printf(),这里编译器使用它的printf语义知识来警告格式错误。这清楚地表明,strcpy()也可以发出警告。

顺便说一句,gcc 警告"abc"[1] = 0;。这很有趣,因为我曾认为strcpy()内在函数会被内联(它必须很短),以便-O3-flto在某个时刻相当于"destination"[i] = "Source"[i];实际上对于编译器是可见的并触发相同的警告。

其他编译器

我测试了VC 2013,gcc 5.3.0,gcc 4.7.2和clang 3.7.1。它们都没有发出将字符串文字传递给strcpy()的警告,但是cremno指出VC提供了捕获错误的/analyze选项。