头文件中定义的Const静态变量在不同的转换单元中具有相同的地址

时间:2013-07-25 02:49:50

标签: c++ static

我已经检查了/usr/include/c++/4.6/bits/ios_base.h中std :: ios :: app的定义,发现std :: ios :: app被定义为const静态变量:

typedef _Ios_Openmode openmode;

/// Seek to end before each write.
static const openmode app =     _S_app;

其中_Ios_Openmode在与

相同的头文件中定义
enum _Ios_Openmode 
{ 
  _S_app        = 1L << 0,
  _S_ate        = 1L << 1,
  _S_bin        = 1L << 2,
  _S_in         = 1L << 3,
  _S_out        = 1L << 4,
  _S_trunc      = 1L << 5,
  _S_ios_openmode_end = 1L << 16 
};

众所周知,静态变量具有内部链接,每个翻译单元都有自己的链接 此静态变量的副本,这意味着不同翻译单元中的静态变量应具有不同的地址。但是,我使用了两个单独的程序来打印std :: ios :: app的地址,发现打印的地址是相同的:

源文件test1.cpp

#include <iostream>

int main() {
    std::cout << "address: " << &std::ios::app << std::endl;
    return 0;
}

结果

address: 0x804a0cc

源文件test2.cpp与test1.cpp相同,结果相同:

address: 0x804a0cc

这真的让我很困惑,不应该在不同的翻译单元中的静态变量有不同的地址吗?


更新:正如评论中指出的那样,std :: ios :: app是静态数据成员而不是静态const变量;静态数据成员具有外部链接,不同翻译单元中静态数据成员的地址应相同。第二点是我验证这个事实的方法是错误的:不同的程序并不意味着不同的翻译单位。

2 个答案:

答案 0 :(得分:3)

您的test1.cpptest2.cpp不仅是两个独立的翻译单元,而且您可以将它们编译为两个完全不同的程序,并将它们作为单独的进程运行。每次运行时,操作系统都会重新定义每个进程的内存空间,并且无法比较每个进程中的地址的绝对值。即使并行运行进程,它们也可能完全相同,因为这些地址是相对于每个进程的虚拟地址空间进行解释的。 (*)

如果要查看内部链接的效果,则需要在编译后将两个翻译单元链接在一起。

您可以通过以下方式执行此操作:

定义标题test.h

const static int i = 0;

print_i_1中定义函数test1.cpp的实现:

#include <iostream>
#include "test.h"

void print_i_1()
{ std::cout << &i << std::endl; }

print_i_2中定义函数test2.cpp的实现:

#include <iostream>
#include "test.h"

void print_i_2()
{ std::cout << &i << std::endl; }

请注意,这两个函数执行相同的操作,但只要它们是单独编译的,它们就会引用i的不同实例。

另请注意,这些程序都不包含main()的定义。我们在第三个文件test.cpp中提供此内容:

extern void print_i_1();
extern void print_i_2();

int main()
{
  print_i_1();
  print_i_2();

  return 0;
}

现在你编译每个.cpp文件(所以我们有三个翻译单元)。我正在使用GCC,但其他编译器也可能有类似的事情:

g++ -W -Wall -g -o test1.o -c ./test1.cpp
g++ -W -Wall -g -o test2.o -c ./test2.cpp
g++ -W -Wall -g -o test.o -c ./test.cpp

然后将它们链接在一起:

g++ -W -Wall -g -o test ./test.o ./test1.o ./test2.o

运行生成的可执行文件test时得到的输出是:

0x4009c8
0x4009d0

两个不同的地址。

请注意,在C ++中实际上并不需要关键字static来完成命名空间范围内的const变量(包括全局命名空间范围)。它们自动具有内部链接,除非明确声明extern


(*)事实证明,您似乎正在使用标准库中定义的类的静态成员的地址。在这种情况下,有两个评论要做:

  • 如果您动态链接到标准库,即使在两个单独的进程之间也可以实际共享对象(但是,由于每个进程仍然有自己的地址,但这仍然不一定意味着显示的地址是相同的空间);
  • 但是,静态类成员具有外部链接,因此在这种情况下,您的假设从一开始就是错误的。

答案 1 :(得分:1)

9.4.2定义静态数据成员的规则:

9.4.2 / 3定义了一个静态const文字`可以指定一个大括号或等于初始值的'。意思是,您可以将其定义为类似于下面的X :: x。

9.4.2 / 4定义只能存在一个定义(“一个定义规则”(odr),见3.2)。

最后,9.4.2 / 5进一步定义了命名空间范围内的类的所有静态数据成员都将具有外部链接。

示例:

// test.h
struct X {
    static const int x = 10;
};

// main.cpp

#include <iostream>
#include "test.h"
void f();
int main(int argc, const char* argv[]) {
    std::cout << &X::x << std::endl;
    f();
    return 0;
}

// test.cpp
#include <iostream>
#include "test.h"

void f() {
    std::cout << &X::x << std::endl;
}

输出:

001A31C4
001A31C4