2

I have checked out the definition of std::ios::app in /usr/include/c++/4.6/bits/ios_base.h and found that std::ios::app is defined as a const static variable:

typedef _Ios_Openmode openmode;

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

in which _Ios_Openmode is defined in the same header file as

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 
};

It's well known that static variable has internal linkage and every translation unit has its own copy of this static variable, which means static variables in different translation unit should have different addresses. However, I have used two separate programs to print address of std::ios::app and found that the printed address are the same:

source file test1.cpp

#include <iostream>

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

result

address: 0x804a0cc

source file test2.cpp is the same with test1.cpp and the result is the same:

address: 0x804a0cc

This really confused me, shouldn't static variables in different translation units have different addresses?


Update: as is pointed out in the comments, std::ios::app is a static data member rather than a static const variable; static data member has external linkage and addresses of static data member in different translation unit should be the same. The second point is that my method for validating this fact is wrong: different program does not mean different translation unit.

Xuwang Yin
  • 23
  • 4
  • dans3itz just pointed out (in a comment on my answer) that `std::ios::app` is in fact a static data member, rather than a static const variable in namespace scope. In that case, it won't have internal linkage. – jogojapan Jul 25 '13 at 03:46
  • Related to http://stackoverflow.com/questions/185624/static-variables-in-an-inlined-function – Mark Ransom Jul 25 '13 at 04:31

2 Answers2

4

Your test1.cpp and test2.cpp are not only two separate translation units, but you compile them into two entirely different programs and run them as separate processes. The memory space for each process is defined by the operating system anew each time you run it, and the absolute values of the addresses in each process cannot be compared. They may be identical even when you run the processes in parallel, because these addresses are interpreted relative to the virtual address space of each process. (*)

If you want to see the effect of internal linkage, you need to link the two translation units together after compiling them.

You can do this in the following way:

Define a header test.h:

const static int i = 0;

Define the implementation of a function print_i_1 in test1.cpp:

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

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

Define the implementation of a function print_i_2 in test2.cpp:

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

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

Note that both functions perform the same operation, but as long as they are compiled separately, they will each refer to a different instance of i.

Note also that none of these programs includes the definition of main(). We provide this in a third file, test.cpp:

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

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

  return 0;
}

And now you compile each .cpp file (so we have three translation units). I am using GCC, but a similar thing is possible with other compilers, too:

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

And then link them together:

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

The output I get when running the resulting executable, test, is:

0x4009c8
0x4009d0

Two different addresses.

Note that the keyword static is not actually required in C++ to accomplish this for const variables in namespace scope (including global namespace scope). They have internal linkage automatically, unless explicitly declared extern.


(*) As it turns out, you seem to be using the address of a static member of a class defined in the standard library. In that case, there are two remarks to be made:

  • If you link to the standard library dynamically, objects can actually be shared even between two separate processes (which, however, still does not necessarily mean that the addresses displayed will be the same, due to each process still having its own address space);
  • However, static class members have external linkage, so your assumptions would have been wrong right from the start in this case.
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • 1
    Awesome answer; and I was going down the same track, until I thought about what std::ios::app was: a class member. – dans3itz Jul 25 '13 at 03:31
  • @dans3itz Interesting. If it's a static data member of a class, it will have external linkage. (I hadn't actually noticed that the declaration was taken from a standard header file. I had assumed the questioner had declared an entirely new variable.) – jogojapan Jul 25 '13 at 03:44
1

9.4.2 defines the rules for static data members:

9.4.2/3 defines that a static const literal `can specify a brace-or-equal-initializer.' Meaning, that you can define it similar to X::x below.

9.4.2/4 defines that only one definition can exist (`one definition rule' (odr), see 3.2).

And finally, 9.4.2/5 further defines that all static data members of a class in namespace scope will have external linkage.

Example:

// 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;
}

Output:

001A31C4
001A31C4
dans3itz
  • 1,605
  • 14
  • 12
  • I am not sure I understand your initial statement. Is this about the one-definition-rule? The rule states that there must be exactly one definition _per translation unit_. But you are right, if they are static class members, they have external linkage (if that's what you mean). It shouldn't matter whether they are of integral or another type. – jogojapan Jul 25 '13 at 03:49
  • I'm looking at the standard right now to find a better definition. – dans3itz Jul 25 '13 at 03:53
  • I still assume that the questioner's intent was to find proof for internal linkage, and picking something that is a class member was simply a mistake. (The actual problem in the question is that the addresses are compared from two different processes.) But if you want a quote from the Standard for static class members having external linkage, best use §9.4.2/5 (in C++11, that is). – jogojapan Jul 25 '13 at 03:55
  • 9.4.2/5 is probably the best definition per C++11; the C++98 standard, which I gladly no longer have, might have had some special scenario coverage. It used to be defined that only integral/enum types could be initialized in-lined and there was some gotchas there as well. – dans3itz Jul 25 '13 at 03:58
  • Are you going to incorporate this information in the answer? I'd be ready to upvote it. Right now it seems to say that the one-definition-rule somehow automatically causes the two instances to become one. In C++03 it's §9.4.2/6. (The special rules for integral types and enums have not changed. But that's about inclusion of initialization in the declaration, not about linkage.) – jogojapan Jul 25 '13 at 04:11