内联函数中的静态变量
我有一个在头文件中声明和定义的函数。 这本身就是一个问题。 如果该函数未内联,则使用该标头的每个翻译单元都会获得该函数的一个副本,并且当它们链接在一起时会有重复。 我“固定”,通过内联函数,但恐怕这是一个脆弱的解决方案,因为据我所知,编译器不保证内联,即使您指定“内联”关键字。 如果不是这样,请纠正我。
无论如何,真正的问题是,这个函数中的静态变量会发生什么? 最终有多少份?
我想你在这里错过了一些东西。
静态功能?
声明一个静态函数会使其在编译单元中“隐藏”。
名称空间范围(3.3.6)的名称具有内部链接,如果它是名称的话
- 显式声明为静态的变量,函数或函数模板;
3.5 / 3 - C ++ 14(n3797)
当名称具有内部链接时,它表示的实体可以通过同一翻译单元中其他作用域的名称来引用。
3.5 / 2 - C ++ 14(n3797)
如果你在一个头文件中声明了这个静态函数,那么包含这个头文件的所有编译单元都将拥有它们自己的函数副本。
问题是,如果函数内部有静态变量,那么包含这个头文件的每个编译单元也会有自己的个人版本。
内联函数?
声明它内联使它成为内联的候选人(现在在C ++中这并不意味着很多,因为编译器会内联或不内联,有时会忽略关键字内联存在或不存在的事实):
带有内联说明符的函数声明(8.3.5,9.3,11.3)声明了内联函数。 内联说明符向实现指示在调用点内函数体的内联替换优于通常的函数调用机制。 在调用点执行这种内联替换不需要实现; 然而,即使省略了这种内联替换,7.1.2定义的内联函数的其他规则仍应遵守。
7.1.2 / 2 - C ++ 14(n3797)
在头文件中,它具有一个有趣的副作用:内联函数可以在同一个模块中多次定义,并且链接器将简单地将它们连接成一个(如果由于编译器原因它们没有被内联)。
对于里面声明的静态变量,标准特别说有一个,只有其中一个:
extern内联函数中的静态局部变量始终指向同一个对象。
7.1.2 / 4 - C ++ 98 / C ++ 14(n3797)
(函数默认是extern,所以,除非你专门将你的函数标记为静态,这适用于该函数)
这具有“静态”的优点(即它可以在头文件中定义)而没有缺陷(如果不是内联,它最多存在一次)
静态局部变量?
静态局部变量没有链接(它们不能在范围之外通过名称引用),但具有静态存储持续时间(即它是全局的,但其构建和销毁遵循特定的规则)。
静态+内联?
然后混合内联和静态会产生你所描述的结果(即使函数是内联的,内部的静态变量也不会),并且你将以包含定义静态函数的编译单元一样多的静态变量结束)。
回答作者的附加问题
由于我写了这个问题,所以我尝试使用Visual Studio 2008进行测试。我尝试打开所有使VS按照标准行事的选项,但是有可能我错过了一些。 这些是结果:
当函数只是“内联”时,只有一个静态变量副本。
当函数是“静态内联”时,有多少份拷贝与翻译单元一样多。
真正的问题是现在是否应该这样,或者这是否是Microsoft C ++编译器的特质。
所以我想你有这样的事情:
void doSomething()
{
static int value ;
}
你必须认识到函数内部的静态变量,简单地说,它是一个全局变量,除了函数的作用域之外都隐藏起来,这意味着只有内部声明的函数才能达到它。
内联函数不会改变任何内容:
inline void doSomething()
{
static int value ;
}
将只有一个隐藏的全局变量。 编译器将尝试内联代码的事实不会改变只有一个全局隐藏变量的事实。
现在,如果你的函数被声明为静态的:
static void doSomething()
{
static int value ;
}
然后它对每个编译单元都是“私有的”,这意味着每个包含声明了静态函数的头文件的CPP文件都有它自己的函数私有副本,包括它自己的全局隐藏变量的私有副本,有包括标题的编译单元。
将“内联”添加到内部具有“静态”变量的“静态”函数中:
inline static void doSomething()
{
static int value ;
}
与不添加此“内联”关键字相同的结果,就内部的静态变量而言。
所以VC ++的行为是正确的,你错误地认为“内联”和“静态”的真正含义。
我相信编译器会创建许多变量的副本,但是链接器会选择一个并让所有其他人参考它。 当我尝试创建不同版本的内联函数时,我的结果类似; 如果函数没有实际内联(调试模式),则所有调用都会转到相同的函数,而不管它们是从哪个源文件调用的。
一会儿就像一个编译器一样思考 - 其他方面怎么可能呢? 每个编译单元(源文件)独立于其他编译单元,可以分别编译; 因此每个人都必须创建一个变量的副本,并认为这是唯一的变量。 链接器可以跨越这些边界并调整变量和函数的引用。
我发现Mark Ransom的回答很有帮助 - 编译器创建了许多静态变量的副本,但链接器选择了一个并在所有翻译单元中执行。
在其他地方,我发现这一点:
见[dcl.fct.spec] / 4
[..]具有外部链接的内联函数在所有翻译单元中应具有相同的地址。 extern内联函数中的静态局部变量始终指向同一个对象。 extern内联函数中的字符串文字是不同翻译单元中的同一个对象。
我没有要检查的标准副本,但它与我在VS Express 2008中检查程序集的经验相符
链接地址: http://www.djcxy.com/p/35317.html上一篇: static variables in an inlined function
下一篇: jQuery counting elements by class; what is the best way to implement this?