![]() |
1
115
我猜你错过了什么,在这里。 静态功能?将函数声明为静态将使其在编译单元中“隐藏”。
如果在标头中声明此静态函数,则包括此标头在内的所有编译单元都将拥有自己的函数副本。 问题是,如果该函数中有静态变量,则包括此标头在内的每个编译单元也将有自己的个人版本。 内联功能?内联声明使其成为内联的候选对象(现在在C++中这并不意味着什么,因为编译器会内联还是不内联,有时会忽略关键字内联存在或不存在的事实):
在头文件中,它有一个有趣的副作用:内联函数可以在同一个模块中多次定义,链接器只需将“它们”连接成一个(如果它们不是由于编译器的原因内联的)。 对于内部声明的静态变量,标准明确规定只有一个:
(默认情况下,函数是extern,因此,除非您特别将函数标记为静态,否则这适用于该函数) 这具有“静态”的优点(即它可以在标头中定义),没有缺陷(如果没有内联,它最多只存在一次) 静态局部变量?静态局部变量没有链接(它们不能在其作用域外通过名称引用),但具有静态存储持续时间(即它是全局的,但它的构造和销毁遵循特定的规则)。 静态+内联?混合内联和静态将产生你所描述的后果(即使函数是内联的,里面的静态变量也不会内联,你最终会得到与编译单元(包括静态函数的定义)一样多的静态变量)。 对作者补充问题的答复
所以我想你有这样的东西:
你必须意识到,函数内的静态变量,简单地说,是一个隐藏在函数范围之外的全局变量,这意味着只有在函数内声明的函数才能访问它。 内嵌该函数不会改变任何事情:
将只有一个隐藏的全局变量。编译器试图内联代码的事实不会改变只有一个全局隐藏变量的事实。 现在,如果你的函数被声明为静态:
然后,对于每个编译单元来说,它都是“私有的”,这意味着每个CPP文件,包括声明静态函数的标头,都将有自己的函数私有副本,包括全局隐藏变量的私有副本,因此变量的数量与包含标头的编译单元的数量一样多。 将“内联”添加到内部有“静态”变量的“静态”函数中:
就内部的静态变量而言,与不添加此“内联”关键字具有相同的结果。 因此,VC++的行为是正确的,你误解了“内联”和“静态”的真正含义。 |
![]() |
2
42
我相信编译器会创建变量的许多副本,但链接器会选择一个并让所有其他副本引用它。当我尝试创建内联函数的不同版本时,我也得到了类似的结果;如果函数实际上没有内联(调试模式),则所有调用都会指向同一个函数,而不管它们是从哪个源文件调用的。 像编译器一样思考一会儿——否则怎么可能呢?每个编译单元(源文件)都是独立的,可以单独编译;因此,每个人都必须创建一个变量的副本,认为它是唯一的一个。链接器能够跨越这些边界,调整变量和函数的引用。 |
![]() |
3
13
我发现Mark Ransom的回答很有帮助——编译器创建了静态变量的许多副本,但链接器选择了一个并在所有翻译单元中强制执行。 在其他地方,我发现了这一点: 请参见[dcl.fct.spec]/4
我没有要检查的标准副本,但它与我在VS Express 2008中检查程序集的经验相匹配 |
![]() |
4
5
应该是这样的。 “static”告诉编译器您希望函数是编译单元的本地函数,因此每个编译单元需要一个副本,每个函数实例需要一个静态变量副本。 “内联”用于告诉编译器你希望函数内联;如今,它只是将其视为“如果有多个代码副本也没关系,只要确保它的功能是相同的”。所以每个人都共享静态变量。 注:此答案是针对原始海报发布给自己的答案而写的。 |
![]() |
5
3
自从我写了这个问题后,我就用Visual Studio 2008进行了尝试。我试图打开所有让VS符合标准的选项,但我可能错过了一些。结果如下: 当函数只是“内联”时,静态变量只有一个副本。 当函数是“静态内联”时,副本的数量与翻译单元的数量一样多。 现在真正的问题是,事情是否应该这样,或者这是否是微软C++编译器的意识形态。 |
![]() |
6
-1
内联意味着可执行代码(指令)内联到调用函数的代码中。编译器可以选择这样做,不管你是否要求它这样做。这对函数中声明的变量(数据)没有影响。 |
![]() |
7
-3
静态意味着一个副本分布在整个程序中,但内联意味着它需要在同一程序中多次使用相同的代码,因此不可能在内联函数中使变量静态。 |
![]() |
8
-3
我相信你最终会得到每个翻译单元一个。实际上,该函数(及其声明的静态变量)有很多版本,每个包含标头的翻译单元都有一个版本。 |
![]() |
9
-4
除了任何设计问题,这一切都可能意味着,既然你已经被它困住了,在这种情况下,你应该使用静态而不是内联。这样每个人都有相同的变量。(静态功能) |