【问题标题】:Visual Studio Debugger VoodooVisual Studio 调试器 Voodoo
【发布时间】:2010-05-14 16:58:46
【问题描述】:

好吧,考虑到我一开始并不真正了解调试器是如何工作的,更不用说“编辑并继续”了,这简直太神奇了。

但我想知道是否有人知道调试器在这种情况下对变量声明做了什么。我可以通过我的代码进行调试,将执行行向前移动 - 通过变量初始声明和分配,代码仍然可以正常运行。如果它是值类型,它将具有默认值,对于 ref 类型,null。

因此,如果我创建一个在声明之前使用变量的函数,它将无法编译,但如果我使用调试器以这种方式运行它,它仍然会运行而不会出错。为什么是这样?这与您不能在声明上放置断点有关吗?

【问题讨论】:

    标签: c# visual-studio debugging


    【解决方案1】:

    是的,这些声明更具结构性。它们是调用方法时分配的堆栈上的局部变量的一部分。你不能打破它们,因为它们并没有真正发生在你写它们的地方——它们不是指令。

    编译器不允许你在声明之前使用它们的原因主要是为了你的理智——你总是知道要查找声明。方法中变量的复杂作用域将进一步说明这一点。

    【讨论】:

      【解决方案2】:

      根据文章Gain performance by not initializing variables

      在 .NET 中,公共语言运行时 (CLR) 明确地在所有变量创建后立即对其进行初始化。值类型初始化为 0,引用类型初始化为 null。

      可能调试器已经知道这些变量,或者因为代码已经编译,或者(现在我正在键入它似乎不太可能,但是)调试器足够聪明,可以检测到变量已被声明。

      【讨论】:

      • 这就是我想知道的。似乎 A) 编译器只是在防御并试图保护我不要在我应该之前使用变量,或者 B) 调试器实际上在做一些不同的事情。你说得对,我一打字就想‘该死的,我真的不希望’;)
      【解决方案3】:

      我认为因为您使用的是调试器,所以您混淆了两种不同的活动,编译和执行,以及两种不同的语句类型,声明性和功能性。

      当你编译时,一个声明性语句告诉编译器为你的变量保留一些内存。它说“哦,你想声明一个名为“wombatCount”的整数;好的,我将使用地址 0x1234 并为你保留四个字节,并在它们上贴一个名为 wombatCount 的标签。这发生在编译期间,早在您运行代码之前。 *

      当您在调试器中执行代码时,您正在运行您的代码,因此它已经知道它为您保留的每个内存字节。变量 wombatCount 已经与地址 0x1234 处的四个字节相关联,因此它可以随时立即访问和更改该数据,而不仅仅是在您的声明语句之后。你的程序当然不能,但调试器可以。

      C# 语言语法要求您在代码中使用内存之前声明内存,但这只是语言定义的一部分,并不是所有编译器的硬性要求。有些语言根本不需要您预先声明变量,甚至还有一些古老的语言,您可以在代码中的任何位置声明变量,而不仅仅是在您将使用它们的“上方”。但是语言开发人员现在明白,语言语法对于人类理解最重要,不再是为了便于机器编码或帮助编译器编写者,因此创建现代语言语法通常是为了尽可能地帮助程序员。这意味着让事情变得不那么混乱,因此“必须先声明”是帮助您避免错误的常用规则。

      (*为了在技术上更正确,我相信在.Net中,标签仅在编译时与将在运行时保留内存的指针列表相关联,但在您使用它们之前不会实际分配数据字节. 差异是内部的,对您的理解不是很重要。重要的一点是声明性语句在编译时提前声明了标签。)

      【讨论】:

      • 那么在运行时,这 4 个字节实际上是什么时候指定的呢?程序第一次启动是什么时候?或者当变量进入范围时,即在声明它的方法的开头?
      • 如果是局部变量或方法参数,则在方法调用时分配。如果变量是引用类型(不是像 int 或 bool 这样简单的东西,也不是 struct),那么当对象是 new()'d 时,内存会在堆上分配
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-08
      • 1970-01-01
      相关资源
      最近更新 更多