内联函数中的静态变量会发生什么情况?

发布于 2024-07-06 18:07:04 字数 256 浏览 13 评论 0原文

我有一个在头文件中声明和定义的函数。 这本身就是一个问题。 当该函数未内联时,使用该标头的每个翻译单元都会获得该函数的副本,并且当它们链接在一起时会出现重复。

我已经通过使函数内联“修复”了这个问题,但恐怕这是一个脆弱的解决方案,因为据我所知,编译器不保证内联,即使您指定inline 关键字。 如果这不正确,请纠正我。

无论如何,这个函数内的静态变量会发生什么? 我最终会得到多少份?

I have a function that is declared and defined in a header file. This is a problem all by itself. When that function is not inlined, every translation unit that uses that header gets a copy of the function, and when they are linked together there are duplicated.

I've "fixed" that by making the function inline, but I'm afraid that this is a fragile solution because as far as I know, the compiler doesn't guarantee inlining, even when you specify the inline keyword. If this is not true, please correct me.

Anyway, what happens to static variables inside this function? How many copies do I end up with?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

人生百味 2024-07-13 18:07:04

我猜你在这里遗漏了一些东西。

静态函数?

将函数声明为静态将使其“隐藏”在其编译单元中。

具有命名空间范围 (3.3.6) 的名称如果是以下名称,则具有内部链接

——显式声明为静态的变量、函数或函数模板;

3.5/3 - C++14 (n3797)

当名称具有内部链接时,它所表示的实体可以被同一翻译单元中其他范围的名称引用。

3.5/2 - C++14 (n3797)

如果在标头中声明此静态函数,则包括此标头的所有编译单元都将拥有自己的函数副本。

问题是,如果该函数内部有静态变量,则包含此标头的每个编译单元也将有自己的个人版本。

内联函数?

将其声明为内联使其成为内联的候选者(现在在 C++ 中这没有多大意义,因为编译器会内联或不内联,有时会忽略关键字 inline 存在或不存在的事实):

带有内联说明符的函数声明(8.3.5、9.3、11.3)声明内联函数。 内联说明符向实现表明,在调用时函数体的内联替换优先于通常的函数调用机制。 不需要实现在调用时执行此内联替换; 然而,即使省略此内联替换,仍应遵守 7.1.2 定义的内联函数的其他规则。

7.1.2/2 - C++14 (n3797)

在标头中,它有一个有趣的副作用:内联函数可以在同一模块中定义多次,并且链接器将简单地将“它们”连接成一个(如果由于编译器的原因,它们没有被内联)。

对于内部声明的静态变量,标准明确规定有一个,而且只有一个:

外部内联函数中的静态局部变量始终引用同一个对象。

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”变量的“static”函数中:

inline static void doSomething()
{
   static int value ;
}

就内部的静态变量而言,与不添加此“inline”关键字具有相同的结果。

所以 VC++ 的行为是正确的,您误解了“内联”和“静态”的真正含义。

I guess you're missing something, here.

static function?

Declaring a function static will make it "hidden" in its compilation unit.

A name having namespace scope (3.3.6) has internal linkage if it is the name of

— a variable, function or function template that is explicitly declared static;

3.5/3 - C++14 (n3797)

When a name has internal linkage , the entity it denotes can be referred to by names from other scopes in the same translation unit.

3.5/2 - C++14 (n3797)

If you declare this static function in a header, then all the compilation units including this header will have their own copy of the function.

The thing is, if there are static variables inside that function, each compilation unit including this header will also have their own, personal version.

inline function?

Declaring it inline makes it a candidate for inlining (it does not mean a lot nowadays in C++, as the compiler will inline or not, sometimes ignoring the fact the keyword inline is present or absent):

A function declaration (8.3.5, 9.3, 11.3) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

7.1.2/2 - C++14 (n3797)

In a header, its has an interesting side effect: The inlined function can be defined multiple times in the same module, and the linker will simply join "them" into one (if they were not inlined for compiler's reason).

For static variables declared inside, the standard specifically says there one, and only one of them:

A static local variable in an extern inline function always refers to the same object.

7.1.2/4 - C++98/C++14 (n3797)

(functions are by default extern, so, unless you specifically mark your function as static, this applies to that function)

This has the advantage of "static" (i.e. it can be defined in a header) without its flaws (it exists at most once if it is not inlined)

static local variable?

Static local variables have no linkage (they can't be referred to by name outside their scope), but has static storage duration (i.e. it is global, but its construction and destruction obey to specific rules).

static + inline?

Mixing inline and static will then have the consequences you described (even if the function is inlined, the static variable inside won't be, and you'll end with as much static variables as you have compilation units including the definition of your static functions).

Answer to author's additional question

Since I wrote the question I tried it out with Visual Studio 2008. I tried to turn on all the options that make VS act in compliance with standards, but it's possible that I missed some. These are the results:

When the function is merely "inline", there is only one copy of the static variable.

When the function is "static inline", there are as many copies as there are translation units.

The real question is now whether things are supposed to be this way, or if this is an idiosyncrasy of the Microsoft C++ compiler.

So I suppose you have something like that:

void doSomething()
{
   static int value ;
}

You must realise that the static variable inside the function, simply put, a global variable hidden to all but the function's scope, meaning that only the function it is declared inside can reach it.

Inlining the function won't change anything:

inline void doSomething()
{
   static int value ;
}

There will be only one hidden global variable. The fact the compiler will try to inline the code won't change the fact there is only one global hidden variable.

Now, if your function is declared static:

static void doSomething()
{
   static int value ;
}

Then it is "private" for each compilation unit, meaning that every CPP file including the header where the static function is declared will have its own private copy of the function, including its own private copy of global hidden variable, thus as much variables as there are compilation units including the header.

Adding "inline" to a "static" function with a "static" variable inside:

inline static void doSomething()
{
   static int value ;
}

has the same result than not adding this "inline" keyword, as far as the static variable inside is concerned.

So the behaviour of VC++ is correct, and you are mistaking the real meaning of "inline" and "static".

亽野灬性zι浪 2024-07-13 18:07:04

我相信编译器会创建该变量的许多副本,但链接器会选择一个副本并使所有其他副本都引用它。 当我尝试创建不同版本的内联函数时,我得到了类似的结果; 如果该函数实际上并未内联(调试模式),则所有调用都会转到同一函数,无论它们是从哪个源文件调用的。

像编译器一样思考一下——否则怎么可能呢? 每个编译单元(源文件)相互独立,可以单独编译; 因此,每个人都必须创建该变量的副本,并认为它是唯一的。 链接器能够跨越这些边界并调整变量和函数的引用。

I believe the compiler creates many copies of the variable, but the linker picks one and makes all the others reference it. I had similar results when I tried an experiment to create different versions of an inline function; if the function wasn't actually inlined (debug mode), all calls went to the same function regardless of the source file they were called from.

Think like a compiler for a moment - how could it be otherwise? Each compilation unit (source file) is independent of the others, and can be compiled separately; each one must therefore create a copy of the variable, thinking it is the only one. The linker has the ability to reach across those boundaries and adjust the references for both variables and functions.

与往事干杯 2024-07-13 18:07:04

我发现 Mark Ransom 的答案很有帮助 - 编译器创建静态变量的许多副本,但链接器选择一个副本并在所有翻译单元中强制执行它。

我在其他地方发现了这个:

参见 [dcl.fct.spec]/4

[..] 具有外部链接的内联函数应具有相同的
所有翻译单元中的地址。 extern 中的静态局部变量
内联函数始终引用同一个对象。 中的字符串文字
extern内联函数是不同翻译单元中的同一个对象。

我没有要检查的标准副本,但它与我在 VS Express 2008 中检查程序集的经验相符

I found Mark Ransom's answer helpful - that the compiler creates many copies of the static variable, but the linker chooses one and enforces it across all translation units.

Elsewhere I found this:

See [dcl.fct.spec]/4

[..] An inline function with external linkage shall have the same
address in all translation units. A static local variable in an extern
inline function always refers to the same object. A string literal in an
extern inline function is the same object in different translation units.

I don't have a copy of the standard to check, but it matches with my experience examining the assembly in VS Express 2008

清晰传感 2024-07-13 18:07:04

应该是这样的。
“static”告诉编译器您希望该函数位于编译单元的本地,因此您需要每个编译单元一份副本以及每个函数实例一份静态变量副本。

“inline”用于告诉编译器您希望该函数被内联; 如今,它只是将其视为“代码有多个副本也没关系,只需确保其功能相同”。 所以每个人都共享静态变量。

注:此答案是针对原发帖者自己发布的答案而写的。

It is supposed to be this way.
"static" tells the compiler you want the function to be local to the compilation unit, therefore you want one copy per compilation unit and one copy of the static variables per instance of the function.

"inline" used to tell the compiler you want the function to be inlined; nowadays, it just takes it as "it's ok if there are several copies of the code, just make sure it's the same function". So everybody shares the static variables.

Note: this answer was written in response to the answer the original poster posted to himself.

梦里南柯 2024-07-13 18:07:04

自从我写下这个问题后,我就用 Visual Studio 2008 进行了尝试。我尝试打开所有使 VS 符合标准的选项,但我可能错过了一些。 结果如下:

当函数只是“内联”时,静态变量只有一份副本。

当函数是“静态内联”时,有多少个翻译单元就有多少个副本。

现在真正的问题是事情是否应该是这样,或者这是否是 Microsoft C++ 编译器的意识形态。

Since I wrote the question I tried it out with Visual Studio 2008. I tried to turn on all the options that make VS act in compliance with standards, but it's possible that I missed some. These are the results:

When the function is merely "inline", there is only one copy of the static variable.

When the function is "static inline", there are as many copies as there are translation units.

The real question is now whether things are supposed to be this way, or if this is an ideosyncracy of the Microsoft C++ compiler.

手心的海 2024-07-13 18:07:04

内联意味着可执行代码(指令)被内联到调用函数的代码中。 无论您是否要求,编译器都可以选择这样做。 这对函数中声明的变量(数据)没有影响。

Inlining means that executable code (instructions) is inlined into the calling function's code. The compiler can choose to do that regardless of whether you've asked it to. That has no effect on the variables (data) declared in the function.

千鲤 2024-07-13 18:07:04

静态意味着一份副本分布在整个程序中,但内联意味着在同一个程序中多次需要相同的代码,因此不可能在内联函数中使变量成为静态。

Static means one copy is distributed throughout the program , but inline means it requires the same code for several time in the same program , so it is not possible to make a variable static inside the inline function.

柠檬色的秋千 2024-07-13 18:07:04

我相信您最终会为每个翻译单元获得一个。 您实际上已经获得了该函数(及其声明的静态变量)的许多版本,每个包含标头的翻译单元都有一个版本。

I believe you will end up with one per translation unit. You've effectively got many versions of that function (and its declared static variable), one for every translation unit that includes the header.

旧人哭 2024-07-13 18:07:04

除了这一切可能暗示的任何设计问题之外,由于您已经坚持使用它,因此在这种情况下您应该使用静态而不是内联。 这样每个人都共享相同的变量。 (静态函数)

Besides any design issues this all may imply, since you're already stuck with it, you should use static in this case not inline. That way everyone shares the same variables. (Static function)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文