5

Given this piece of code:

#include <list>
(void) someFunction(void) {
    list <int> l;
    l.push_back(1);
}
  • Where are the elements of the list stored? Stack? Heap?
  • How can I do to empirically check that values are in stack or heap?
  • This function can returns the list? Edited If I declare function as list, can function returns list with out problems?

Sample (returning list):

#include <list>
list<int> someFunction(void) {
    list <int> l;
    l.push_back(1);
}

...
l2 = someFunction();
l2.push_back(2);
dani herrera
  • 48,760
  • 8
  • 117
  • 177
  • 6
    The actual list object will be stored on the stack (as all local variables and function arguments), while the items in the list will be on the heap. – Some programmer dude Jan 16 '12 at 15:49
  • 2
    Why do you care where they are stored? – Pubby Jan 16 '12 at 15:50
  • 1
    Pubby, stack data will be removed at end of function. – dani herrera Jan 16 '12 at 15:53
  • 3
    @danihp You are confusing stack/heap with automatic/dynamic storage. Don't. – Pubby Jan 16 '12 at 15:56
  • as to your second question, you can as long as you change the signature to return by value (or smart pointer), currently it's void... – Nim Jan 16 '12 at 15:57
  • @JoachimPileborg, If list is on stack and elements on heap. How clean heap elements from stack when function ends? list destructor? – dani herrera Jan 16 '12 at 16:06
  • 2
    Agreed with Pubby: You are worrying about the wrong thing. The real question to ask is whether there are any dynamic objects (yes: the list nodes are dynamic), and whether you need to worry about them (no: the list destructor takes care of them). – Kerrek SB Jan 16 '12 at 16:07
  • @Someprogrammerdude could you please edit the question and add your answer also inside the question. You point out an important thing. – Meric Ozcan Nov 28 '21 at 10:40

7 Answers7

6

Where are the elements of the list stored? Stack? Heap?

List elements are stored on the heap. You can see this following your debugger down on the push_back method call. The easiest to see it is to store objects rather than POD type, and log the constructor. You'll need a copy constructor as they are get copied. The allocation happens with the template argument allocator, which you can specify or without specifying it it would go with the default heap allocation.

How can I do to empirically check that values are in stack or heap?

You can check this with push_back elements from the stack:

std::list<int> my_list;
int a = 10;
my_list.push_back(a);
a = 11;
assert(*my_list.begin() == 10);

This function can returns the list?

In C++ there are two ways to pass data around: by reference or by value. If you're function looks like this,

list<int> func()
{
  list<int> res;
  res.push_back(10);
  return res;
}

then you're passing the list by value, which means that the compiler will call the list's copy constructor which also copies all the values in the list. As the function returns, after it copies the list, the "res" list's destructor will be called, releasing all its elements. However if you do this:

list<int>& func()
{
      list<int> res;
      res.push_back(10);
      return res;
}

You're code will fail as you return the reference to your "res" list, which will be already destroyed at the end of its scope, so you're reference will be invalid.

The problem with the first solution can be performance. You can also do that without the call of the copy constructor like this:

void func(list<int>& res)
{
  res.push_back(10);
}

list<int> list_to_fill;
func(list_to_fill);

In this case, there's no copying and it should be faster as there's only one list creation.

progician
  • 910
  • 8
  • 15
  • You are incorrect about the copy constructor being called when returning the list. The returned list has the same reference as the one inside the function call due to compiler optimizations. See my answer. – CompEng88 Jul 15 '15 at 16:50
3

Where are the elements of the list stored? Stack? Heap?

The elements held by the list are typically dynamically allocated so this will be on the heap.

This function can returns the list?

No it cannot, you declared your function with a return type of void.

How can I do to empirically check that values are in stack or heap?

The only way to be really sure is to peek into the std::list implementation to see what it's really doing. But you don't really need to care about this since it's an implementation detail.

greatwolf
  • 20,287
  • 13
  • 71
  • 105
  • I know that function is declared as void, but, If I declare function as list, can I return list with out problems? – dani herrera Jan 16 '12 at 15:59
  • 1
    yes you can certainly do that but you'll have to return it by value. This might entail an expensive copy operation depending on how complex your list usage is and number of elements in it. However, if you permit using C++11 you can elide the copying with std::move semantics. – greatwolf Jan 16 '12 at 16:02
  • Perhaps it is possible to know if it is on heap or stack with an external tool? Or printing pointer position? – dani herrera Jan 16 '12 at 16:03
  • There are certainly memory tools you can employ for checking heap usage on your program(eg. Valgrind). Printing pointer address won't be that useful since it's highly OS/Platform dependent -- there's no certain way to distinguish what a stack vs heap address would look like. – greatwolf Jan 16 '12 at 16:10
  • It's probably more useful to indicate what you're trying to accomplish. What does knowing if something's stack allocated vs heap do for you? – greatwolf Jan 16 '12 at 16:12
  • Victo T., just to know. Is enougth for you ;) – dani herrera Jan 16 '12 at 16:20
2

The answer you selected is incorrect about 1 thing...

returning a list does not call the list's copy constructor.

list<int> func()
{
  list<int> res; // &res returns 0x7fff183f0900
  res.push_back(10);
  return res;
}

list<int> l = func();   // &l also returns 0x7fff183f0900 (no copy)

You can double check this by printing &res in the function and &l outside the function.

The compiler is optimized to pass through returned objects without making copies, otherwise every single non-pointer object returned would be copied, which would be insane.

CompEng88
  • 1,336
  • 14
  • 25
  • Note that tis is only guaranteed by the standard in C++17 and above. See https://en.cppreference.com/w/cpp/language/copy_elision for reference. – florestan Aug 26 '19 at 07:54
  • I believe my test was done using C++11, but, you're right, the compiler may not always be able to optimize the copy until C++17. The simple cases like above should optimize in C++11, every time though. – CompEng88 Aug 28 '19 at 18:15
1

You could do it this way (as long as I understand your problem)

#include <iostream>
#include <list>

using namespace std;

list<int> & fun()
{
    list<int> & temp = *(new list<int>);
    temp.push_back(4);
    // you can do other operations on the list
    return temp; // you pass the reference, any list will not be destroyed neither copied.
}

int main()
{
    list<int> & my_list = fun();

    cout << *(my_list.begin()) << endl;
}
1

If you changed the function signature to return a list, you could indeed return the list; however the compiler would make a copy of the list, and the copy is the one that would be received by the caller.

The original list will be destroyed at the end of the function, but the copy will be made before that happens so you will return valid data. If you try to cheat the system and return a pointer or reference instead to avoid the copy, that's when you'll run into undefined behavior.

The list itself will be on the stack, but the elements contained within will be on the heap. The list will contain pointers that it manages behind the scenes so that you don't have to worry about the storage at all.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • With any luck, move semantics would allow the list to be returned by value without touching the dynamically allocated list nodes. – Kerrek SB Jan 16 '12 at 16:17
  • @KerrekSB, is there any way to guarantee move semantics or are you at the mercy of your compiler's optimizations? – Mark Ransom Jan 16 '12 at 16:29
  • Is there a way to test this empirically? Printing element address? – dani herrera Jan 16 '12 at 16:30
  • @danihp: Just make an object whose constructors, assigners and destructor print a message, and then check how often a list element gets made, moved, assigned or destroyed. – Kerrek SB Jan 16 '12 at 16:45
  • @MarkRansom: The standard actually mandates in the general "container requirements" that move-construction have constant complexity (Table 99). – Kerrek SB Jan 16 '12 at 16:51
  • @KerrekSB, nice idea. But the list may be have only pointer to this objects and in copy process only pointer will be copied. I can't beleave that complier know how to create a object from another one calling constructors and destructors. – dani herrera Jan 16 '12 at 19:58
1

Some of it can vary depending on the whim of the compiler and/or library author.

As it is right now, there's a pretty fair chance the compiler will detect that the result of creating and populating the list is never used, so it would eliminate the entire function as dead code. To prevent that, you might (for example) return the list instead of just letting it be destroyed at the end of the function.

That only adds new possibilities though. Most compilers implement Return Value Optimization (RVO) and Named Return Value Optimization (NRVO). These basically mean that when/if you return a value (such as your list object) instead of creating an object on the stack and copying it to the destination when you return it, the compiler generates code to generate the object where it's going to be assigned after return. In this case, instead of creating a local function at all, the function will just receive a hidden pointer to the location where it will write the result.

A std::list normally allocates space for the data to be stored using std::allocate. You can, however, specify a different allocator for it to use. In this case, the space could be allocated almost anywhere.

Although it's more common with other (sort of) containers like std::string, it's also possible (in at least some cases) to store at least a small amount of data in the object itself, and only allocate space on the heap when/if that overflows. There are some restrictions on this to maintain exception safety and such, but in the case of list<int> it shouldn't be a problem. For example, the first ten ints might be stored in the list object itself, and only when/if you add more than that, it would allocate space on the heap.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Thanks a lot for your answer. Is there a way to test that L in function and L in main is the same list empirically? Printing list address or elements address? – dani herrera Jan 16 '12 at 16:34
  • @danihp: you could print out addresses of items in the list. If they match between the function and main, that would at least tell you the *contents* of the list wasn't be copied during return. You might still have separate list objects, but that's nearly irrelevant (a `list` object will typically contain little more than a pointer or two). – Jerry Coffin Jan 16 '12 at 16:51
  • nice Jerry, would you like to append your post code to print the pointers? – dani herrera Jan 16 '12 at 19:55
1

A return function is declared like this : list<int> somefunc() {list<int> L; return L; } .

Check that values with list<int>::iterator .

Like this :

#include <iostream>
#include <list>
using namespace std;


list<int> someReturnFunc(int listSize)
{
    list<int> myList;

    for (int g=0; g< listSize; g++)  myList.push_back(g);

    return myList;
}

int main ()
{
    list<int> yourList;
    list<int>::iterator i;

    yourList = someReturnFunc(15);

    for(i=yourList.begin(); i != yourList.end(); ++i) cout << *i << " ";

    cout << endl;

    return 0;

}
Software_Designer
  • 8,490
  • 3
  • 24
  • 28
  • Is there a way to test that L in function and L in main is the same list empirically? Printing list or element address? – dani herrera Jan 16 '12 at 16:32