静态局部变量如何沿不同的翻译单元共享?

How can static local variable shared along different translation unit?

提问人:YoonSeok OH 提问时间:4/30/2023 最后编辑:YoonSeok OH 更新时间:5/1/2023 访问量:375

问:

静态局部变量如何沿不同的翻译单元共享?

我知道“静态”指定内部链接

如果“静态”成员函数在不同的转换单元中具有相同的地址,则每个转换单元都有该函数的副本,链接器选取要包含在可执行文件中的副本之一

然后,在两个不同翻译包含 1 个共享头的场景中,该头有一个带有静态成员函数的类,该函数递增静态局部变量,并且每个翻译单元 (cpp) 调用共享头的静态成员函数,每个翻译单元都会递增自己独立的静态局部变量,因为 static 指定了内部链接。例如

共享.h

#pragma once

class CShared
{
public:

    static inline void Foo()
    {
        static int s_nCount = 0;
        ++s_nCount;
    }

private:

    CShared(){}
    ~CShared(){}
};

#pragma once
class A
{
public:

    A(){}
    ~A(){}

    void Foo();
};

.cpp

#include "A.h"
#include "Shared.h"

void A::Foo()
{
    CShared::Foo();
}

B.h

#pragma once
class B
{
public:

    B(){}
    ~B(){}

    void Foo();
};

B.cpp

#include "B.h"
#include "Shared.h"

void B::Foo()
{
    CShared::Foo();
}

main.cpp

#include <iostream>
#include "A.h"
#include "B.h"

int main()
{
    A a;
    B b;

    a.Foo();
    b.Foo();

    return 0;
}

结果是 s_nCount 当 b.Foo() 变为 2。因此,静态局部变量是“共享的”,而不是每个翻译单元都有自己的静态局部变量副本,沿着不同的翻译单元。这难道不意味着静态局部变量不是内部链接吗? “不同翻译单元之间共享”和“内部链接”不是冲突的术语吗?

任何澄清这一点的人都会受到真正的赞赏。

[更新]

到目前为止,似乎,

静态的 联动
CShared::Foo 内部链接以外的东西
s_nCount 外部链接(Mike Nakis,The Dreams Wind)或无链接(来自莫斯科的Vlad)

根据一些程序员的家伙的说法,

静态的 联动
CShared::Foo 无链接 (cppreference)
s_nCount 无链接 (cppreference)

在我的理解中, CShared::Foo 属于“无链接 - 本地类及其成员函数” s_nCount属于“无链接 - 未显式声明为 extern 的变量(无论 static 修饰符如何)”

谁能得出这个结论?

[更新2]

静态的 联动
CShared::Foo 外部链接
s_nCount 无联动

关于成员函数前面的“静态”含义

“static - 静态或线程存储持续时间和内部链接(或不在匿名命名空间中的静态类成员的外部链接)。”

“静态类成员具有外部链接。类成员函数具有外部链接。

关于“全局”变量前面的“静态”含义

关于“局部”变量前面的“静态”含义

C++ 静态 链接 storage-duration translation-unit

评论

1赞 Some programmer dude 4/30/2023
像 C++ 中的几乎所有其他内容一样,的含义取决于上下文。如果在命名空间范围内定义静态变量(如全局变量),它将具有内部链接,并且不会从其转换单元导出。静态块局部变量(如函数中的变量)的生命周期从块的第一次执行到进程结束,并且对于块的所有执行都是共享的。这是有据可查的,应该由任何像样的书籍、教程或课程来教授。static
0赞 user12002570 4/30/2023
A.h您的问题中缺少标题。
1赞 YoonSeok OH 4/30/2023
@Jason 感谢您的评论。我会把它添加到问题中。
2赞 john 4/30/2023
@YoonSeokOH 对于局部变量,不会出现链接问题。链接器永远不会看到它们。
1赞 Some programmer dude 4/30/2023
en.cppreference.com/w/cpp/language/storage_duration#no_linkage“在块范围内声明的以下任何名称都没有链接:......未显式声明的变量(无论 static 修饰符如何);”(强调我的)因此,在块中声明的变量是否具有并不重要,只要它没有被声明,因为它没有链接。externstaticextern

答:

4赞 Mike Nakis 4/30/2023 #1

我认为您可能会对全球范围感到困惑。 在 .staticstaticclass

事实上,您链接到的 wikibooks.org 页面就是这样说的。我引用:

这导致“static”关键字仅在实现文件中使用,从不在头文件中使用,除非在头文件的类定义中使用“static”,其中它表示内部链接以外的其他内容。

因此,如果您只是在全局范围内拥有它:;然后会有内部联动。static int a = 0a

但是,如果你有,那么它只是一个类变量(即,不是一个实例变量),并且它具有外部链接。class A { static int a = 0; }a

评论

0赞 user17732522 4/30/2023
"正常链接“将是外部链接。对我来说,一个比另一个更正常并不明显。(好吧,除了也可以给类内部链接。
0赞 Mike Nakis 4/30/2023
@user17732522你是对的,我纠正了这一点。
0赞 YoonSeok OH 4/30/2023
@Mike Nakis 感谢您的回答。哦。所以类内的静态,例如我示例中的静态局部变量,不是内部链接。换句话说,它是外部链接。
1赞 YoonSeok OH 4/30/2023
我只是想知道“内部链接以外的东西”是什么意思,没有链接或外部链接。
3赞 Vlad from Moscow 4/30/2023 #2

在此成员函数声明中

static inline void Foo()
{
    static int s_nCount = 0;
    ++s_nCount;
}

局部变量没有内部链接。它不是命名空间变量。它是一个声明的局部变量,没有存储类说明符。它是一个局部变量,具有静态存储持续时间,没有链接。因此,该函数只有一个局部变量,该变量仅初始化一次,并在函数的每次调用中更改。s_nCountextern

来自 C++17 标准(6.5 程序和链接)

3 具有命名空间范围 (6.3.6) 的名称具有内部链接,如果它 是...

8 本规则未涵盖的名称没有关联。此外,除了 如前所述,在块范围 (6.3.3) 声明的名称没有链接。

和(10.1.6 内联说明符)

  1. ....[ 注意:具有外部链接的内联函数中的静态局部变量始终引用同一对象。定义的类型 在具有外部链接的内联函数的主体中是 每个翻译单元中的类型相同。— 尾注 ]

下面是在块作用域中声明的具有内部链接的变量的示例。

#include <iostream>

static int n;

void f()
{
    extern int n;

    ++n;
}

int main()
{
    for ( size_t i = 0; i < 5; i++ )
    {
        std::cout << ( f(), n ) << ' ';
    }

    std::cout << '\n';
}

程序输出为

1 2 3 4 5

因此,在全局命名空间中声明的带有存储类说明符的变量具有内部链接。在函数的块作用域中声明的局部变量表示(引用)在全局命名空间中声明的具有内部链接的变量。nstaticnfn

评论

0赞 user12002570 4/30/2023
存储持续时间和链接是不同的东西。
0赞 Vlad from Moscow 4/30/2023
@Jason 你的评论与我的回答无关,
0赞 YoonSeok OH 4/30/2023
来自莫斯科的@Vlad 感谢您的回答。所以,静态局部变量“s_nCount”是有外部链接的,它只是一个具有静态存储时长的局部变量。很高兴您提供 C++17 标准。
0赞 Vlad from Moscow 4/30/2023
@YoonSeokOH 局部变量 s_nCount 没有链接。
0赞 YoonSeok OH 4/30/2023
@Vlad莫斯科,我刚刚检查了“未显式声明为 extern 的变量(无论静态修饰符如何)”。如果它不是外部的,您能否解释一下 B.o 中的 s_nCount 如何反映 A.o 中 s_nCount 的修改?如果它们不是外部链接,我不明白这一点。
3赞 The Dreams Wind 4/30/2023 #3

static在不同的上下文中应用关键字时,可以具有不同的含义。 局部变量旁边的关键字引入了静态存储持续时间,但不会引入内部链接(就像关键字对全局变量所做的那样)。static

只要声明局部变量的函数具有外部链接(成员函数是外部链接),所有转换单元都共享相同的局部变量。如果函数具有内部链接,则所有转换单元都将具有自己的局部变量实例(例如,如果您在类声明之外定义了它):staticstaticstaticstatic

#pragma once

// Defined in the same header outside of the class body
static void Foo()
{
    static int s_nCount = 0;
    ++s_nCount;
}

class CShared
{
// ..Class definition goes here.. //
}
....

评论

0赞 YoonSeok OH 4/30/2023
Thanks for your answer. Now I got clarified that static local variable is not internal linkage, rather it just means storage duration.