【问题标题】:file scope and static floats文件范围和静态浮点数
【发布时间】:2010-12-14 23:14:10
【问题描述】:

我在我的一个 AI 项目中遇到了一个有趣的问题。我正在尝试格式化一些调试文本,但发生了一些奇怪的事情。这是一段代码:

    float ratio = 1.0f / TIME_MOD;

TIME_MOD 是一个静态浮点数,在单独的文件中声明。该值是根据另一个类中的用户输入进行修改的(我已经验证该值在“输入”函数的范围内仍在调试时已更改),但是每当我尝试在外循环中除以它时,我都会得到相同的号码。 (1除以TIME_MOD的初始值)。

我是否遗漏了有关静态变量和文件范围的内容?

【问题讨论】:

  • 我不认为你可以外部静态变量。因此我没有在“比率”文件中声明它。
  • 向我们展示TIME_MOD的声明
  • 澄清一下,在我解决问题之前,声明是“static float TIME_MOD = 0.1f;”
  • 在这种情况下,它是具有内部链接的浮点变量的定义。每个翻译单元都有自己的浮点数,称为“TIME_MOD”。不要在全局和命名空间范围内使用static。它没有做你认为它做的事情。

标签: c++ memory static scope


【解决方案1】:

我认为“静态”这个词有些混淆。我们有一个关键字static,它在不同的上下文中做不同的事情,我们使用“静态”这个词来命名三类“存储持续时间”之一。在某些情况下,static 不控制对象的存储持续时间,而只控制“链接”,这可能是造成混淆的主要原因。


存储时间

存储持续时间是对象的属性。

  • 具有静态存储持续时间的对象的内存只分配一次。初始化取决于对象的类型和定义的位置。一旦它被初始化,它通常会保持活动状态,直到 main 的执行结束。您在全局/命名空间范围内声明和定义的对象总是具有静态存储持续时间。

  • 具有自动存储期限的对象只能在函数中的块内定义。这样的对象是在执行到达定义时创建的。这可能会发生多次(递归),从而创建多个对象。当执行离开块时,对象会自动销毁。

  • 动态分配的对象具有动态存储持续时间。在这种情况下,用户通过 new、new[]、delete、delete[] 等来控制对象的生命周期。


联动

内部链接与外部链接是关于跨翻译单元的名称的可见性。如果您使用外部链接声明某些内容,则您会引入一个名称,该名称也可以在其他翻译单元中使用,以引用同一实体,只要这些其他 TU 包含正确的声明(通常包含在头文件中)。如果您使用内部链接定义某些内容,则无法从另一个翻译单元按名称访问它。您甚至可以定义多个具有相同名称的实体(每个 TU 一个),只要您的外部链接不超过一个即可。


关键字“静态”

static 的效果取决于上下文:

  • 如果您在全局/命名空间范围内声明或定义对象,则它始终是具有“静态存储持续时间”的对象。在全局/命名空间范围内使用关键字static 根本不会影响存储持续时间。相反,它会影响链接。它声明实体——也可能是一个函数——具有内部链接。因此,存储类说明符已被“滥用”来做一些完全不同的事情:强制执行内部链接。在这种情况下,它与extern 有点相反。在 C++ 中,您可以使用匿名命名空间实现相同的效果。我们鼓励您更喜欢匿名命名空间而不是 static,以“尽量减少混淆”。

  • static at class scope 可用于在类范围内声明具有静态存储持续时间的对象。只有一个这样的变量,而不是每个对象都有一个。

  • static 在函数范围内可用于声明具有延迟初始化的静态存储持续时间的对象


如果您说“静态变量”,则不清楚您的确切含义。你是指“静态存储时长”还是“内部联动”?

如果您想跨翻译单元共享“全局”变量,您必须在头文件中声明它作为具有外部链接和定义的实体 在一个翻译单元中。注意没有使用关键字static

// myheader.hpp
extern int k; // declaring an int variable with external linkage

// foo.cpp
#include "myheader.hpp"
int k;        // defining an int variable with external linkage

// bar.cpp
#include "myheader.hpp"
int main() {
    return k;
}

【讨论】:

  • 我熟悉“静态”的类和函数范围,但我无法理解您的第一个要点。通过“静态存储持续时间”,您是否只是说在程序终止之前变量不会超出范围?我使用 extern 来访问数据,例如大型硬编码数组或在另一个文件中定义的设备上下文。我假设“extern int k”将允许以下文件范围内的对象引用包含文件中定义的“int k”?我喜欢你的回答。我只是想确保我完全理解它。
  • @Rantaak:我添加了更多内容作为对您评论的回应。
  • 您需要修改您的描述,因为它仍然令人困惑(我知道您想说什么)。在要点中仅描述关键字的作用。 “静态存储时长”是一个独立于关键字的概念,应在单独的段落中单独描述。
  • 我重写了答案。我希望它不那么令人困惑:)
  • @Rantaak:“静态存储持续时间”意味着它始终存在,而不是始终在范围内。范围和存在不是同义词。不要包含定义,只有声明。头文件通常不应该是即时的,只是声明性的。即时定义应存在于单独编译和链接的文件中。当然,无论如何你都应该避免使用所有这些全局数据。
【解决方案2】:

静态变量仅存在于当前编译单元中。从其定义中删除静态并将其更改为“volatile”(尽管这假设您正在使用多个线程,否则您不需要使用 volatile),一切都应该没问题。

编辑:根据您的回答,我假设您有如下代码。

A.cpp:

static float TIME_MOD = <some value>;

B.CPP:

static float TIME_MOD = <some value>;

如果您这样做,那么 TIME_MOD 存在于 2 个地方,这就是您的问题的根源。你需要像这样重写代码。

A.cpp:

float TIME_MOD = <some value>;

B.CPP(以及 C.CPP、D.CPP 等):

extern float TIME_MOD;

然后像往常一样使用 TIME_MOD。这告诉编译器 TIME_MOD 在其他地方,不用担心不知道它包含什么。然后,链接器将通过并将此浮动 TIME_MOD 定义“链接”到正确的定义。

还值得指出的是,使用“extern float TIME_MOD;”可能是可行的。在某个头文件中,并将其包含在您需要它的任何 CPP 文件中。仍然将实际定义(即非外部定义)保存在一个文件中,并且只有一个文件。

这当然可以解释这样一个事实,即我认为你正在外部化一个静态(我认为这是不可能的)。

【讨论】:

  • 如果我让它变得易变,我会收到一堆链接器错误,告诉我它已经在 Camera.obj 中定义(我正在修改值的类)。我的一位教授告诉我,应该避免使除布尔值以外的任何东西都变为 volatile,但我不记得他的推理。你知道为什么 Visual Studio 认为我对这个 volatile float 的赋值是重新定义吗?
  • 不是没有看到你的代码......你实际上是在外部 TIME_MOD 还是你只是在多个编译单元中将它定义为一个静态并希望它们以某种方式都指向相同的值?
  • 我在一个文件中有一个静态声明。没有外线电话。您不能外部静态数据。问题已解决。请参阅我的回答帖子。
【解决方案3】:

您可以将静态变量更改为#define,并在单例中设置一个与其相等的成员变量。修改和访问将应用于单例的成员变量。

【讨论】:

  • 所以..这并不能解释答案。很想知道你做错了什么......
【解决方案4】:

静态变量在内部链接。您无法访问在其他源文件中定义的静态变量。
如果您在某些头文件中定义了静态变量 TIME_MOD,则可能会发生您遇到的情况。在输入和比率源文件中包含相同的头文件,因此两个文件都有变量 TIME_MOD 的私有副本, 现在输入模块更改了 TIME_MOD 值,但它修改了自己的私有副本,因此比率文件中的值保持不变,因此您的行为。

现在,如果是这种情况,您不需要静态 TIME_MOD,并且要解决名称冲突,您可能希望使用命名空间。

【讨论】:

    【解决方案5】:

    我猜你在头文件中声明了这个变量:static float TIME_MOD; 并将此文件包含在 cpps 中。通过这样做,您有效地在每个编译单元中创建了相同命名变量的单独实例。 您应该将声明更改为: extern float TIME_MOD; 并在其中一个 cpps 中定义变量:float TIME_MOD = 0;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 2022-11-12
      • 1970-01-01
      相关资源
      最近更新 更多