-3

I have a question about memory management and global variables versus heap and how to decide whether to use a variable whose space is allocated from the heap instead of global variable.

I understand that variables allocated from the heap using new last the whole lifetime of the program and that global variables also last the lifetime of the program.

Should heap variables be used instead of the global variables?

For example of these two approaches below, which is more appropriate, in terms of code speed and memory management, and why would the approach be more appropriate:

#include <iostream>

int x = 5;

int main(int argc, char** argv)
{

   // do stuff with the variable x

   return 0;
}

vs

#include <iostream>

int main(int argc, char** argv)
{
   int x = new int;
   *x = 5;

   // do stuff with the variable pointed to by x

   delete x;
   return 0;
}
trincot
  • 317,000
  • 35
  • 244
  • 286
  • 7
    Better for *what*? There are no absolutes – StoryTeller - Unslander Monica Apr 03 '19 at 12:11
  • code speed, memory management, maybe code safety. – Javis Johnson Apr 03 '19 at 12:16
  • use for what? in most of the cases use the stack instead: safer, quick and easier to use. – olivecoder Apr 03 '19 at 12:16
  • 3
    Why do you need global variables? – Rietty Apr 03 '19 at 12:18
  • 1
    And again, there are **no absolutes**. There is no silver bullet that will make every bit of code in the world "better" in every metric. – StoryTeller - Unslander Monica Apr 03 '19 at 12:18
  • I would not allocate a single integer on the heap. You don't really gain from that. To access it you still need a pointer to the integer and that probably is the same size or larger than the heap allocated integer. – drescherjm Apr 03 '19 at 12:21
  • 2
    Globals are generally results of bad design choice. – Moia Apr 03 '19 at 12:21
  • Someone once asked: What is the best name prefix for global variables? The aswer is: "//" – Dimfred Apr 03 '19 at 12:26
  • 4
    First, why do you need to choose between two bad options? Don't dynamically allocate if you can avoid it, don't use global variables if you can avoid it. When you *need* dynamic allocation, use that. When you *need* a global variable, use that. Second, these two concepts are orthogonal. One does not replace the other, one is not an alternative to the other. There is no reason to have to choose between the two. – François Andrieux Apr 03 '19 at 12:35

7 Answers7

8

Using heap or using global variables are not really alternatives. Actually if possible do not use either of them:

#include <iostream>
int main()
{
   int x = 5;
}

There is no apparent reason why x should be global and there is no apparent reason to use manual memory allocations, hence just use a local variable with automatic storage.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

It's the best to try to avoid global variables as much as possible. The heap is the best place to store data that takes up a lot of space. If you store to much data on the stack you will get a stack overload. In this simple example you obviously don't have to think about stack overload. But if you have to choose between the examples I would use the heap.

Siebe Dreesen
  • 25
  • 1
  • 8
1

Deciding the memory allocation used for a variable or object is tied to the life time of the variable (when should it be created and when should it be destroyed), how long it needs to exist as well as what needs to access and use the variable.

So far as memory management is concerned, the less the human programmer tries to do it and the more that the programmer relies on the compiler to handle the details, the more likely your program is going to work.

Your particular examples are too simplistic to illustrate the actual heuristics and considerations for choosing how to allocate the memory for a variable. The main problem with your examples is that there are no other functions provided as part of the examples so no reason why a global should be used in the first place.

So lets just talk about the idea of where variables reside and some considerations about that decision in general terms.

In C++ most programs use three types of memory allocation: (1) static variables, (2) auto variables, and (3) heap variables.

Static variables are created at the time the program begins running and last as long as the program ends. However the visibility of the variable can vary between global visibility, file visibility, or within a particular scope.

int x = 5;  // static, global variable created when the program starts.
static int x2 = 7;  // static, file visible variable created when the program starts.

int main ()
{
    // the visibility of the following static variable is not global but only
    // within the scope of main() but it exists for the life time of the program.
    static int y = 3;   // static, local variable created when the program starts

    x = 12;

    {     // create a new scope
        static int y2 = 18;  // static, local variable visible only within this scope
        // some other stuff
     }   // end of scope, y2 is no longer visible but it still exists.

    // other stuff
    return x;
}

Auto variables or what are commonly called stack variables are created at the time the thread of execution reaches the point where the variable is defined. These variables last as long as the thread of execution is within the scope where the variable is defined. As soon as the execution leaves the scope, the variable is destroyed.

int main ()
{
    int x = 5;    // Auto variable created when this line of code is reached.

    {             // new scope created within this function
        int x2 = 3;  // Auto variable created when this line of code is reached
         // other stuff
    }             // end of new scope, auto variable x2 is destroyed

    //   other stuff
    return x;
}    // end of function scope, auto variable x is destroyed.

Heap variables are created at the time the new operator is used to create the variable. They last until the variable is destroyed using the delete operator.

int main ()
{
    int *xp = new int;   // heap variable created and held in pointer

    {                        // new scope
       int *xp2 = new int;   // heap variable created and held in auto pointer variable
       int *xp3 = new int;   // heap variable created and held in auto pointer
       // other stuff
       delete xp2;           // heap variable destroyed
     }                // auto pointer variables xp2 and xp3 destroyed. heap variable whose address was in xp3 is NOT destroyed. memory leak.

    return x;
}

Note: Notice an important point in the above sample program, the heap variable (the memory allocated for a variable from the heap) is different from the pointer variable used to hold the address provided by the new operator. Should the pointer variable go out of scope, is destroyed, before the heap variable is destroyed using the delete operator, the heap variable will continue to exist even though the ability to access it is gone with the destruction of the pointer variable that contained its address.

You can also run into a problem should you delete the heap memory and then try to use the address contained in the pointer variable after doing so. Once you do a delete you no longer own the memory allocated with the new operator. It belongs to the memory allocator at that time.

Using smart pointers such as std::unique_ptr was introduced to address the mistakes made of a pointer going out of scope before the memory it pointed to was deleted or deleting memory and they trying to use the address in the pointer later. By using one of the smart pointer types for your pointer variable, the compiler will generate the necessary code to perform the delete for you when the smart pointer goes out of scope.

To Summarize

In all three types mentioned there are some costs that are borne by all three: cost of construction, cost of destruction. These costs are encumbered at different times. And in some cases the cost may be only once and sometimes multiple times.

Where global variables really shine is when it is a const that holds a value that never changes and is used in various places in the program. The construction and destruction are only once, when the program begins. Since the variable is never changed, there are nothing other than runtime costs for any methods used by the object.

An Auto variable in a function is going to be constructed and destructed every time the function is entered into and then left. The same applies to a Heap variable that is created with the new operator every time the function is entered and destroyed before the function is left. So the characteristics of Auto and Heap variable are very similar in this scenario.

Both an Auto variable and a Heap variable must be passed to any other function that will be using the variable. With a global variable this passing of the value or address is not required since the global variable is visible to the function being called. This brings up the related topic of how variables are passed to functions, Pass by Value and Pass by Reference. See What's the difference between passing by reference vs. passing by value?

If you are using heap variables, you must handle more of the details about managing memory so you are more likely to introduce a defect by doing so. Smart pointers such as std::unique_ptr can help but there is still something that has to be managed and thought about.

Using auto variables is easier on the programmer since the compiler handles when they are created and when they are destroyed. And the compiler will provide warnings and errors about whether a particular auto variable exists or not or is visible or not.

There is some runtime overhead to using heap variables with the creation and destruction that may or may not be a consideration. In other words using a heap variable means that you must use the memory allocator to create it with the new operator as well as to destroy it with the delete operator. Actually using the heap variable may or may not be that costly in terms of machine time. There are a whole host of considerations involving virtual memory and memory access time that must be considered there.

On the other hand auto variables are typically allocated quickly but in a limited size memory area, normally the stack. So if you need a large memory area then an auto variable is probably not a good solution and one of the other types would be preferable.

Static variables are allocated and created once, when the program begins. The problem with static variables is that what was last put there is what will be there for the next time it is accessed. There is only one copy of the variable that is shared by what ever parts of your code are using it. This sharing is why there can be some many defects introduced into program by using global variables.

Finally one thing you need to remember is that predicting what code the compiler will generate from the source lines you provide can be pretty difficult. Modern optimizing compilers will do a lot of moving about and elimination as well as behind the scenes generation of code.

Visibility of variables and Coupling

As you can see in the above, simple examples, there are two considerations about the creating of variables. One is the lifetime of the variable, how long it will be needed. The other is the visibility of the variable, what parts of your program can see the variable and access it.

A global static variable can be seen by all parts of your program including source code that is in other files which are compiled and linked together to create your program. A file static variable can be seen by all parts of your program in the same source file.

When you create a variable that is shared by multiple parts of your program you need to consider what is called inter-module coupling. Using globally visible variables is a well documented source of defects. The greater the visibility of a variable, the more moving parts of your program that can access a variable, the more likely you are to introduce a defect.

Human beings are not good at dealing with intricate, complex systems and understanding their operation and behavior and predicting what they will do. Using global variables in large programs results in exactly this kind of system, the kind that human beings aren't very good at understanding.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
1

Depends. I usually prefer global variables but here’s the trade offs.

Global variables use static memory allocation, almost always better than dynamic. No out of memory errors are possible, the memory for them is allocated at compile-time by linker. No need to worry about memory leaks, address space fragmentation, and other issues that come with dynamic memory management.

Global variables inflate binary size. If you need to keep megabytes of data in them, dynamic allocation will work better, e.g. a statically allocated global std::vector or std::unique_ptr

If the data in the variables is mutable, accessing global variables from more than one place can make the code very hard to work on, especially to debug. Only relevant for medium to large projects. Simple workaround, make them static, and/or put them in anonymous namespaces, and pass references instead of directly accessing them from different functions.

One more thing. If you’re coding an application not a library, and it’s single threaded, for mutable global stuff consider using local variables in the scope of your main function. Stack allocation doesn’t use dynamic memory either.

Soonts
  • 20,079
  • 9
  • 57
  • 130
  • 1
    You may want to talk about or at least mention static initialization order fiasco. – drescherjm Apr 03 '19 at 12:53
  • @drescherjm if you require specific initialization order, just use Meyers’ singletons. The C++ runtime guarantees both construction and destruction order for such global objects, even across compilation units. – Soonts Apr 03 '19 at 12:55
  • I mentioned that because its an additional potential pitfall that new users may not yet know about. – drescherjm Apr 03 '19 at 12:57
  • @drescherjm I think dynamic memory management contains way more pitfalls. Also the sample code in the question allocates an integer, and doesn’t obviously require any initialization order. – Soonts Apr 03 '19 at 12:59
0

You should use

static (global variable storage) for vars needed the entire run of the program.

stack (local variable) for vars needed in your function.

heap (dynamic storage) for a large pool of memory, dynamic variables, etc

cprogrammer
  • 5,503
  • 3
  • 36
  • 56
0

If you absolutely need global variables, here's one way to do it:

#include <iostream>

auto& x()
{
  static auto data = 5;
  return data;
}

int main() 
{
  std::cout << x() << std::endl;
  return 0;
}

This way you can use std::lock_guard or something similar to avoid the race condition in multi-threaded applications and it gives you more flexibility.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
0

Limit the globals as much as you can.

That said, i have seen plenty of stupid code passing, for example, a top level Window handle from a function to a function just to avoid setting a simple global variable (similar to repeating std:: 100 times in a function because 'using namespace std' is "bad").

There is no Swiss army; use globals when you have to. You can use them also in a namespace or as static class members to avoid the global namespace (but again, having a top hwnd in a namespace and keep using myapp:hwnd is also weird).

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78