C++ 为什么在C++;?

C++ 为什么在C++;?,c++,pointers,undefined-behavior,delete-operator,C++,Pointers,Undefined Behavior,Delete Operator,考虑以下计划: #include <iostream> int main() { int b=3; int* a=&b; std::cout<<*a<<'\n'; delete a; // oops disaster at runtime undefined behavior } #包括 int main() { int b=3; int*a=&b; std::cout不可能在编译时确定指针指向什么,下面是一个示例来

考虑以下计划:

#include <iostream>
int main()
{
    int b=3;
    int* a=&b;
    std::cout<<*a<<'\n';
    delete a;  // oops disaster at runtime undefined behavior
}
#包括
int main()
{
int b=3;
int*a=&b;

std::cout不可能在编译时确定指针指向什么,下面是一个示例来说明这一点:

volatile bool newAlloc;

int main()
{
   int b=3;
   int* a;
   if(newAlloc)
   {
       a = new int;
   } else {
       a = &b;
   }
   std::cout<<*a<<'\n';
   delete a;  // impossible to know what a will be
}
volatile bool-newAlloc;
int main()
{
int b=3;
int*a;
if(newAlloc)
{
a=新整数;
}否则{
a=&b;
}

std::cout在一般情况下,不可能确定指针在编译时是否有效。例如,如果库中有一个函数以指针作为参数,则编译器无法确定它将始终具有传递给它的有效指针

该标准将删除无效指针视为未定义的行为,否则每次删除指针或取消引用时都必须在运行时进行检查,这将导致语言设计者不希望的性能损失

<为什么C++的实现不提供任何编译器错误或任何警告?< /P> 叮当作响的静态分析器做到了这一点

对于您的代码片段,您可以获得:

$ scan-build clang++ main.cpp
scan-build: Using '/usr/bin/clang' for static analysis
main.cpp:7:5: warning: Argument to 'delete' is the address of the local variable 'b', which is not memory allocated by 'new'
    delete a;  // oops disaster at runtime undefined behavior
    ^~~~~~~~
1 warning generated.
scan-build: 1 bug found.

如前所述,在编译时并不总是能够确定指针是否有效,但静态分析器绝对可以帮助您解决这个问题。

以下是实现细节的答案:

new
/
delete
实现必须有某种方法来跟踪有关通过
new
分配的所有块的一些数据,特别是关于它们的大小。通常,这是通过在分配的块开始之前将这些数据存储在字节中来完成的。
delete
的实现可以非常快速地y确定如何处理该块。当然,如果传入无效地址,
delete
实现只会在那里找到虚假数据,可能会导致应用程序崩溃或悄悄损坏数据。但这没关系,因为传入无效地址是未定义的行为

现在,将传递无效指针合法化会产生什么后果?
new
/
delete
实现将无法仅从传入的指针中查找此信息,因为这可能是虚假信息。检索必要信息的唯一方法是在当前有效指针表中查找给定指针。eve说,这样的查找非常昂贵n如果使用快速查找算法;无论如何,这比仅仅取消对给定指针的引用要慢得多


这一切都是关于速度/安全性的权衡,C++在这个例子中选择了速度。< /P>你想定义什么行为?你想要一个定义的、保证的崩溃吗?有什么有用的目的?@阿贝伦基:如果定义好的话,保证每次崩溃都会更好。不是吗?一些编译器警告…标准。rd是定义正确的程序如何正确运行,而不是简单地调试坏程序。您要求的行为会使开发和调试更容易,但会使正确的程序更慢、更大或更复杂。有些情况很容易发出警告,有些编译器也会发出警告。我几乎可以猜测基因还可以静态地解决RAR问题。如果指针是32位,程序在其生命周期中创建42亿个以上的对象,那么一些无效指针将无法与有效的指针区分开来。对于32位寻址的机器来说,使用96位指针来提供几乎完全安全是可能的。(每个包含32位句柄、该句柄的32位分配计数和32位偏移量),或者对于具有64位寻址但最大对象大小为4 gig的机器,使用类似的128位指针格式,但速度会有很大的损失(可能是2-5倍左右)。如果您在用户定义函数中编写代码并将其作为内联函数,则可以在编译时计算分支条件。当内联函数的主体在调用方函数端被替换时,智能编译器可以对其执行特定于上下文的优化。因此,编译器现在可以专注于分支预测。如果您说ne只是一个请求,然后您可以通过在gcc编译器中使用_属性_uu((始终为u内联))强制编译器进行内联,VC++编译器也有一个强制内联的选项。@PravasiMeet是真的,在很小的情况下,可以做出这样的假设(一些静态分析器会指出这一点).但可能的病例数量和性质都不多,这并不能成为有用或可靠的诊断方法。