Thread: More questions about constants

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Feb 2003
    Posts
    596

    More questions about constants

    1. There seem to be a number of opinions about how a const int (NOT a class member) should be declared & defined.

    For example, in this thread
    http://cboard.cprogramming.com/showt...ts+header+file
    Elysia says "Don't put any variables in headers. Define them in a .cpp file and use extern in the header files."

    In this one, Daved recommended defining a constant in a header file but enclosed in an unnamed namespace, ChaosEngine says to define it in a header file (wrapped in a #ifndef ... #endif block) as static const int, and CornedBee says that neither the static nor the namespace is necessary.
    http://cboard.cprogramming.com/showt...nstants+header

    I didn't find any clear statement on this from either Salem or Prelude. I'd love to get some further guidance on this -- is a consensus possible?


    2. Sort of related to the above, does "static" have any significance in C++ when used OUTSIDE of any class definition? Is there any difference between a global "static const int x" vs. a global "const int x"?


    3. This
    http://www.parashift.com/c++-faq-lit....html#faq-29.7
    sums up the reasons to prefer using const over #define, but it also says that there are cases when #define is preferable. So, when is #define preferable?

  2. #2
    Its hard... But im here swgh's Avatar
    Join Date
    Apr 2005
    Location
    England
    Posts
    1,688
    #define is the C version of a C++ constant. I would say the only time I would use #define is in a header file. I cannot find a reason why you would need to use it in a .cpp source file. constants more or less replaced #define in the C++ language when defining a variable to be constant ( read only ).

    Every book I have read says #define on a variable is "evil" but that is their view not every single programmers. Just my two cents on the matter.
    Double Helix STL

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by R.Stiltskin View Post
    For example, in this thread
    http://cboard.cprogramming.com/showt...ts+header+file
    Elysia says "Don't put any variables in headers. Define them in a .cpp file and use extern in the header files."
    The reason I mentioned that is because you'll get linking errors if you put variables in headers directly.
    There may be several approaches to this, but what I'd do is use define in a header or make the constants in a .cpp file and declare them as extern in the header files.
    That way you avoid linking errors.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> In this one, Daved recommended defining a constant in a header file but enclosed in an unnamed namespace.

    Please note that I wasn't recommending doing that as a solution to the problem. I was merely stating that if you choose to make a static const int at namespace scope that you should use an unnamed namespace instead.

    I'd trust CornedBee's recommendation to use neither static nor an unnamed namespace. I am not sure whether "extern" is required to avoid linking errors if you are declaring a constant.

    >> 2. does "static" have any significance in C++ when used OUTSIDE of any class definition?
    Yes. It means that the variable (or constant) is created for that particular compilation unit. If you put it in a source file, then functions in that source file can access it and you will not have any problems if there are similar names in other compilation units.

    If you put it in a header file, it acts the same way, except it does so for each source file that includes that header (including the source files that include the header indirectly). This is why CornedBee mentioned that using static for a constant has the disadvantage of creating a duplicate for each source file that includes the header where it was declared.

    Note that using static this way is deprecated in favor of an unnamed namespace, as I mentioned in the linked thread.

    >> So, when is #define preferable?
    I don't know of any situation where it would be preferable when a const variable or enum will also work. You can use #defines for pre-processor things like header include guards or conditional compilation.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Daved pointed out that "the use of the static keyword is deprecated when declaring objects in namespace scope", so that rules out ChaosEngine's suggestion. I note that the C++ Standard states in section 7.3.1.1: "The use of the static keyword is deprecated when declaring objects in a namespace scope (see annex D); the unnamed-namespace provides a superior alternative." This leads to Daved's suggestion, but I believe CornedBee is right: the unnamed/anonymous namespace means that there is a constant with the same name and value but in a different namespace in each file, thus it is duplicated across the program.

    Consequently, I think that the use of extern as suggested by Elysia may be the best option, possibly when in a namespace. (At least excluding other options like enums and class members.)
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Thanks to all of you.

    I did some testing (not that I don't trust you guys) & it's clear that static in this context gives the variable file scope so when I put static int x in a header file & #include that header in 2 different source files, each source file gets a different variable named x whose value can be changed in one sourcefile & remain unchanged in the other. So, as laserlight & Daved said, putting a constant static const int x = 5 in a header & #including that in multiple source files results in multiple constants all named x with the value 5 & just wastes space (unless for some reason we want different variables named x with different values at different places in a program). I don't understand the point of an unnamed namespace -- static gives this behavior without any namespace in the code.

    I also found that if the variable is not static, header include guards provide no protection (I thought they would); when I #include a header that defines int j = 8 in two different source files, g++ gives a "multiple definition" error. Thinking about it, the reason is obvious -- if that header is used to compile two separate object files it's going to create a separate global variable in each of them so clearly that creates a problem when it gets to the linker. I'm surprised I haven't had a problem with this before -- just lucky I guess.

    So Elysia's solution seems to be the best since it avoids the compiler's multiple definition errors, allows selective inclusion in only the source files where the variable (constant or otherwise) is needed, and avoids using the "nasty" #define.

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> I don't understand the point of an unnamed namespace -- static gives this behavior without any namespace in the code.
    And an unnamed namespace gives that behavior without any static in the code. The short answer is that you should prefer an unnamed namespace over static because the C++ standard tells you to. I don't know the long answer of why the standards committee made that decision without looking it up again. (Also remember that I'm only comparing instances where you will be using either static or an unnamed namespace, which is not the case in the example you are discussing in this thread).

    >> I also found that if the variable is not static, header include guards provide no protection
    A header include guard provides protection against compilation errors, since it stops a header from being included twice in the same compilation unit. It does not stop linker errors, since those come from the linker trying to combine the code in different compilation units.

    The question I still have is whether the extern is necessary for constants.

  8. #8
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Quote Originally Posted by Daved View Post
    The question I still have is whether the extern is necessary for constants.
    It seems not to be. Actually, I have no trouble getting it to work without extern, and I was also able to compile with extern const int j declared in a header and defined in the "main" source file. But I can't figure out how to compile with the constant declared as extern in a header and defined in a separate source file. This set of files gives me two "undefined reference to 'j'" errors. (It compiles all 3 ".o" files, but doesn't link them.) Can you tell me what's wrong?

    Code:
    #ed: commented out the following 2 lines as they were not serving any purpose in this makefile
    #CXX = g++
    #CCFLAGS = -g -Wall
    
    scopetest:	source1.o	source2.o	constants.o
    	g++	source1.o	source2.o	constants.o	-o scopetest
    
    source1.o:	header1.h	header2.h	source1.cc
    	g++	-c	source1.cc
    
    source2.o:	header1.h	header2.h	source2.cc
    	g++     -c	source2.cc
    
    constants.o:	constants.cc
    	g++	-c	constants.cc
    
    clean:
    	rm *.o scopetest
    Code:
    // header1.h
    
    #ifndef header1_h
    #define header1_h
    
    static int i = 4;
    extern const int j;
    //const int j = 6;
    
    int dosomething();
    
    #endif
    Code:
    // header2.h
    
    namespace {
    const int c = 5;
    }
    Code:
    // source1.cc
    
    #include "header1.h"
    #include "header2.h"
    #include <iostream>
    using namespace std;
    
    //const int j = 6;
    
    int main() {
      dosomething();
      ++i;
      dosomething();
      cout << "in main, i = " << i << endl;
      cout << "in main, c = " << c << endl;
      cout << "in main, j = " << j << endl;
      dosomething();
    
    }
    Code:
    // source2.cc
    
    #include "header1.h"
    #include "header2.h"
    #include <iostream>
    using namespace std;
    
    int dosomething() {
      cout << "in source2.cc, i = " << i << endl;
      cout << "in source2.cc, c = " << c << endl;
      cout << "in source2.cc, j = " << j << endl;
      return i;
    }
    Code:
    // constants.cc
    
    const int j = 6;
    Last edited by R.Stiltskin; 01-02-2008 at 10:51 PM.

  9. #9
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Duh!

    I needed #include "header1.h" in constants.cc. It should be
    Code:
    // constants.cc
    
    #include "header1.h"
    
    const int j = 6;
    With that correction it compiles & runs correctly. Or, I can eliminate constants.cc and constants.o from the makefile and in header1.h replace
    Code:
    extern const int j;
    with
    Code:
    const int j = 6;
    and it compiles & runs correctly that way as well.

    So, as Daved suggested, it seems that extern is not necessary for a constant.

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Code:
    // a1.cc
    #include <iostream>
    
    const int x = 5;
    
    int main()
    {
       std::cout << x << std::endl;
    }
    Code:
    // a2.cc
    const int x = 6;
    Compile and link these two together.

    1) Does you compiler/linker allow it?
    2) If so, what value does it print?
    3) Explain.
    4) Add a function to a2.cc which prints x, and call that from a1.cc. What happens?

    This exercise should help you figure out how C++ treats "const" at global scope.
    Last edited by brewbuck; 01-02-2008 at 10:58 PM.

  11. #11
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Quote Originally Posted by brewbuck View Post
    ...
    Compile and link these two together.

    1) Does you compiler/linker allow it?
    No problem: g++ a1.cc a2.cc compiles & links them. (I'm using g++ 4.1.2.)
    2) If so, what value does it print?
    5
    3) Explain.
    It seems that each x has file scope.
    4) Add a function to a2.cc which prints x, and call that from a1.cc. What happens?
    My modified version of the program is below. When print() (in a2.cc) is called from main() it prints 6.
    Code:
    // a1.cc
    #include <iostream>
    extern void print();
    
    //const int x = 5;
    int x = 5;
    
    int main()
    {
      std::cout << x << std::endl;
      print();
    }
    
    // a2.cc
    #include <iostream>
    
    const int x = 6;
    //int x = 6;
    //extern int x;
    
    void print() {
      std::cout << "print: " <<  x << std::endl;
    }
    I tried a few other variations: when I remove const from both definitions, I get a "multiple definition" error, even if a value is assigned in only one file. If either one of the xs is declared const, or if both of them are, then the program compiles/links without error and main() and print() each have their own (different) version of x so each x seems to have file scope.

    The downside of having been taught not to use global variables is that I don't actually know how, but it seems that the only way to make a single (non-const) variable x global and accessible to all files is by using extern. Is this correct?

    CornedBee:
    I am still not sure what you are suggesting in the case of constants, but now it seems that defining them in a header file and including that in each source file that needs access to the constant is preferable because this method ensures that each such file has the same value assigned to its constant, it avoids the extern shortcomings that you pointed out, and, if I understand you correctly, the linker will merge multiple copies of a constant into one if they all have the same value, which they will. (Clearly, this does not happen when each file defines the same const symbol with a different value.)
    Do you agree?

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by R.Stiltskin View Post
    Code:
    extern void print();
    That's not necessary. You can remove the extern for function declarations.

    The downside of having been taught not to use global variables is that I don't actually know how, but it seems that the only way to make a single (non-const) variable x global and accessible to all files is by using extern. Is this correct?
    Yes, that is correct.

    ...if I understand you correctly, the linker will merge multiple copies of a constant into one if they all have the same value, which they will. (Clearly, this does not happen when each file defines the same const symbol with a different value.)
    Do you agree?
    No, this is not always the case. Ponder the following output:
    Address of n is Help.cpp: 0040225C
    Address of n in Help2.cpp: 0040223C
    From this simple code:
    Code:
    // Help.h
    const int n = 10;
    
    // Help.cpp
    #include "Help.h"
    int main()
    {
    	cout << "Address of n is Help.cpp: " << &n << endl;
    	Help();
    }
    
    // Help2.cpp
    #include "Help.h"
    void Help()
    {
    	cout << "Address of n in Help2.cpp: " << &n << endl;
    }
    So clearly, as you see, there are two variables with the same number, even in release.
    Same goes even if it's static.
    Same with an unnamed namespace.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Elysia View Post
    No, this is not always the case. Ponder the following output:


    From this simple code:
    Code:
    // Help.h
    const int n = 10;
    
    // Help.cpp
    #include "Help.h"
    int main()
    {
    	cout << "Address of n is Help.cpp: " << &n << endl;
    	Help();
    }
    
    // Help2.cpp
    #include "Help.h"
    void Help()
    {
    	cout << "Address of n in Help2.cpp: " << &n << endl;
    }
    So clearly, as you see, there are two variables with the same number, even in release.
    Same goes even if it's static.
    Same with an unnamed namespace.
    Have you considered that the very act of taking the address could be what is preventing the folding from occurring? Given that the address is the only observable difference between the constants, taking it would make the folding observable, and thus prevent it from occurring. Just a thought.

    Very quantum.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by R.Stiltskin View Post
    I don't see how you got that to run without <iostream> and std:: for cout & endl, but with those modifications, yes, g++ 4.1.2 also ends up with 2 different addresses for n, so this seems to contradict CornedBee's statement.
    I left that out.
    The "real" code would be, for Help2.cpp:
    Code:
    #include "stdafx.h"
    //#include <Stuff/NewThread.h>
    #include "Help.h"
    
    using namespace std;
    
    void Help()
    {
    	cout << "Address of n in Help2.cpp: " << &n << endl;
    }
    Quote Originally Posted by CornedBee View Post
    Have you considered that the very act of taking the address could be what is preventing the folding from occurring? Given that the address is the only observable difference between the constants, taking it would make the folding observable, and thus prevent it from occurring. Just a thought.

    Very quantum.
    Then what do you propose to find out if there's one or multiple constant variables?
    Even with assembly, there will still be two copies.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    I don't see how you got that to run without <iostream> and std:: for cout & endl, but with those modifications, yes, g++ 4.1.2 also ends up with 2 different addresses for n, so this seems to contradict CornedBee's statement.

    Here is the only way that I have found so far that produces a single global constant (a single address) defined in a separate file:

    Code:
    // Help.h
    extern const int n;
    
    // Help.cpp
    #include "Help.h"
    #include <iostream>
    
    void Help();
    
    int main()
    {
      std::cout << "Address of n is Help.cpp: " << &n << std::endl;
      Help();
    }
    
    // Help2.cpp
    #include "Help.h"
    #include <iostream>
    
    void Help()
    {
      std::cout << "Address of n in Help2.cpp: " << &n << std::endl;
    }
    
    
    //Help3.cpp
    // this file can contain all constant definitions
    
    #include "Help.h"
    
    const int n = 10;

    ed: I didn't see CornedBee's last post until after I posted this one. Re the "quantum" comment, it's an interesting thought, but what are we to make of that? i.e. How can one rely on a compiler to behave a certain way, when attempting to verify it results in different behavior? In short, do you recommend following the approach I stated in the last paragraph of #17?
    Last edited by R.Stiltskin; 01-03-2008 at 12:53 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C interview questions
    By natrajdreams in forum C Programming
    Replies: 7
    Last Post: 12-12-2010, 12:40 PM
  2. questions....so many questions about random numbers....
    By face_master in forum C++ Programming
    Replies: 2
    Last Post: 07-30-2009, 08:47 AM
  3. A very long list of questions... maybe to long...
    By Ravens'sWrath in forum C Programming
    Replies: 16
    Last Post: 05-16-2007, 05:36 AM
  4. Several Questions, main one is about protected memory
    By Tron 9000 in forum C Programming
    Replies: 3
    Last Post: 06-02-2005, 07:42 AM
  5. Trivial questions - what to do?
    By Aerie in forum A Brief History of Cprogramming.com
    Replies: 23
    Last Post: 12-26-2004, 09:44 AM