Thread: More questions about constants

  1. #16
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Yes, but all modern linkers perform constant folding, merging these into one.

    And as I said, one is a const expression, the other not. Observe:
    Code:
    const int CON1 = 5;
    extern const int CON2;
    
    template <int I>
    class foo
    {
    };
    
    int main()
    {
    	int ar_yes[CON1];
    	int ar_no[CON2];
    
    	foo<CON1> t_yes;
    	foo<CON2> t_no;
    }
    Code:
    $ g++ -c -pedantic -std=c++98 src.cpp
    src.cpp: In function ‘int main()’:
    src.cpp:12: error: ISO C++ forbids variable-size array ‘ar_no’
    src.cpp:15: error: ‘CON2’ is not a valid template argument for type ‘int’ because it is a non-constant expression
    src.cpp:15: error: invalid type in declaration before ‘;’ token
    The -std and -pedantic arguments are necessary to disable GCC's VLA for C++ extension.
    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

  2. #17
    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?

  3. #18
    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.

  4. #19
    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

  5. #20
    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.

  6. #21
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    How can one rely on a compiler to behave a certain way, when attempting to verify it results in different behavior?
    Suppose that CornedBee says is true (and it might be). Does silently folding the constants affect the behaviour of the code? No, it does not, so it is safe. Only when it is unsafe then it is not done, so it does not matter when it is safe.
    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

  7. #22
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Another thought: constant folding is done by the compiler, not the linker, right? If I understand correctly, each of the .cc and .cpp files in our examples is a separate compilation unit, and if so, why would we expect constants defined in separate files to be merged?

  8. #23
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Another thought: constant folding is done by the compiler, not the linker, right?
    In this case it would be performed by the linker, if it is done at all. Other types of constant folding (e.g., evaluating constant arithmetic expressions or substituting constant expressions) would be done by the compiler.
    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

  9. #24
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Quote Originally Posted by laserlight View Post
    Suppose that CornedBee says is true (and it might be). Does silently folding the constants affect the behaviour of the code? No, it does not, so it is safe. Only when it is unsafe then it is not done, so it does not matter when it is safe.
    I think that even before CornedBee brought us into Schroedinger's world, this discussion had become more theoretical than practical. I agree that in most cases the extra memory consumed by having multiple copies of some constants may be trivial. The question is, is it necessary?

  10. #25
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by R.Stiltskin View Post
    i.e. How can one rely on a compiler to behave a certain way, when attempting to verify it results in different behavior?
    It is perfectly well-defined behavior. "const" tells the compiler it is allowed to fold the constant when possible. If it is declared "extern," it cannot fold the constant because it has been explicitly declared to reside elsewhere. And if you take the address of a constant, this also forces the compiler to create an actual variable so that it can have an address.

    The change in semantics of "const" in C++ was meant to DISCOURAGE the use of #define. You were supposed to be able to replace a #define with a const declaration, even in a header file. In other words, the "new const" was meant to act very much like a #define replacement. The examples given here show that declaring a global/namespace const variable in a header file is usually okay, because we don't typically take the address of a simple constant or use "extern."

    Maybe it's confusing, but the comparison with quantum physics was meant partially in jest (I assume) -- there's no random behavior here, you just have to understand it.
    Last edited by brewbuck; 01-03-2008 at 01:15 PM.

  11. #26
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by R.Stiltskin View Post
    Another thought: constant folding is done by the compiler, not the linker, right? If I understand correctly, each of the .cc and .cpp files in our examples is a separate compilation unit, and if so, why would we expect constants defined in separate files to be merged?
    The linker can fold as well. It does so whenever it throws out duplicate template instantiations. Also, most link and executable formats have the concept of a "weak symbol" which can be defined in multiple modules -- all the definitions are merged into one during link time. In the past when linkers did not support such folding, there were complicated pre-link steps required to weed out multiple template instantiations, or other compiler-specific weirdness to get it to work.

    However, as we've already shown here, most const definitions don't even get created as actual symbols in the object code. Unless they are declared "extern" or you take their address, that is.

  12. #27
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    I think the quantum reference was that by simply attempting to observe the behavior you are changing it.

    I guess the bottom line is that simply creating a constant variable in a header is generally the best option, there is no need for extern, static or an unnamed namespace.

  13. #28
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Quote Originally Posted by Daved View Post
    I think the quantum reference was that by simply attempting to observe the behavior you are changing it.

    I guess the bottom line is that simply creating a constant variable in a header is generally the best option, there is no need for extern, static or an unnamed namespace.
    I'll be happy to take that as the final answer, unless others object...

  14. #29
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    I guess the bottom line is that simply creating a constant variable in a header is generally the best option, there is no need for extern, static or an unnamed namespace.
    I agree, since it appears that the extern version is a minor optimisation that may already be done by the linker.
    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

  15. #30
    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.

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