how to return vectors

Hello,

I am writing a library, my first one.
I need some help how to return vectors from this library?

If the library is allocating memory to return things, whose responsibility would it be to free or destruct those allocations? The library or the program linking against it?

How should I return a vector of strings from a function?
How should I return a vector of structs contain strings or vectors of string from a function?

The example code below compiles without error but segfaults.
I also have questions in the comments of the code.

Thanks in advance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <iostream>
#include <vector>
#include <string>

using namespace std;

typedef struct somestruct{
    // should these be string or string* ?
    string name;
    string desc;
    string something_else;
    // should this be vector < string >    ,
    //                vector < string >*   , or
    //                vector < string* >*  ?
    vector < string > some_list;
} somestruct;

vector < string > foo(string a, int n){
    // for foo("A", 5) it should return
    // [0] A
    // [1] AA
    // [2] AAA
    // [3] AAAA
    // [4] AAAAA
    // it actually returns...
    // [0] AAAAA
    // [1] AAAAA
    // [2] AAAAA
    // [3] AAAAA
    // [4] AAAAA
    vector < string > retvec;
    for(int i = 0 ; i < n ; i++){
        // should I be using "new" here somehow?
        string tmp(a);
        for(int j = 0 ; j < n ; j++){
            tmp.append(a);
        }
        retvec.push_back(tmp);
    }
    // what is being returned here?
    // am I returning a pointer to the locally defined retvec?
    return retvec;
}

vector < somestruct > bar (){
    // should return ...
    //
    // [0]->name           : name-0
    // [0]->description    : description-0
    // [0]->something_else : something_else-0
    // [0]->some_list[0]   : help-0
    // [0]->some_list[1]   : me-0
    // [0]->some_list[2]   : please-0
    // [1]->name           : name-1
    // [1]->description    : description-1
    // [1]->something_else : something_else-1
    // [1]->some_list[0]   : help-1
    // [1]->some_list[1]   : me-1
    // [1]->some_list[2]   : please-1
    //
    // ... but it segfaults before
    vector < somestruct > retvec;
    
    char buf[100];
    
    for(int i = 0 ; i < 3 ; i++){
        somestruct *item = (somestruct*)malloc(sizeof(somestruct));
        
        sprintf(buf, "name-%d", i);
        item->name = string(buf);
        
        sprintf(buf, "description-%d", i);
        item->desc = string(buf);
        
        sprintf(buf, "somethine_else-%d", i);
        item->something_else = string(buf);
        
        sprintf(buf, "%d", i);
        item->some_list.push_back(string("help-")   + string(buf));
        item->some_list.push_back(string("me-")     + string(buf));
        item->some_list.push_back(string("please-") + string(buf));
        
        // is this correct?
        retvec.push_back(*item);
    }
    
    return retvec;
}

int main(int argc, char* argv[]){
    vector < string > result1;
    vector < somestruct > result2;
    
    result1 = foo("A", 5);
    
    cout << "result1 size : " << result1.size() << endl;
    for(int i = 0 ; i < result1.size() ; i++){
        cout << "result1[" << i << "] : '" << result1[i] << "'" << endl;
    }
    
    cout << endl;
    
    result2 = bar();
    
    cout << "result2 size : " << result2.size() << endl;
    for(int i = 0 ; i < result2.size() ; i++){
        cout << "result2[" << i << "]" << endl;
        cout << "  " << result2[i].name           << endl;
        cout << "  " << result2[i].desc           << endl;
        cout << "  " << result2[i].something_else << endl;
        for(int j = 0 ; j < result2[i].some_list.size() ; j++){
            cout << "  " << result2[i].some_list[j] << endl;
        }
    }
    
    
    return 0;
}
Your segfault is being caused by line 67. You cannot malloc() somestructs because you need
somestruct's default constructor to run in order to default construct the members. You would
have to use new.

However, you don't need any dynamic memory allocation at all here. First, the return types of
your functions are correct.

Lines 67-84, you can just use a stack-allocated somestruct instead of dynamically allocating one.

EDIT: also, as a matter of programming technique, it is not necessary in C++ to do "typedef struct
foo { ... } foo;". Simply doing "struct foo {...};" is sufficient in C++.
Last edited on
Great...thanks.

I changed...
somestruct *item = (somestruct*)malloc(sizeof(somestruct));
... to ...
somestruct *item = new somestruct;

... and my code ran and completed without segfaulting.

As for returning that vector of strings, I thought that the returned vector had nothing but references to the same string since they were all "AAAAA" but it turns out it was a bug in my code because I had j < n rather than j < i.

So now that it seems I have a working example, I need to ask about memory allocations.

My call to bar() is causing a memory leak.
If I wrap my call to bar() in a large for loop and I watch the top command in another terminal I can see my memory usage going up steadily.

How do I free up these vectors that are being returned?
I tried free and delete with result2 and &result2 and either got compile errors or glibc runtime errors.
I tried running .clear() on result2 after every call and it was still eating up memory.
I tried looping through all of result2's items and running .clear() on .some_list and then running .clear() on result2 and it was still eating up memory.

So, again I ask... whose responsibility is it to free up memory? The library itself or the app linking against that library?
The leak is because you are doing new somestruct and never deleting it. The fix for this is simply to make item a stack-based object, ie:

1
2
3
4
5
6
for(int i = 0 ; i < 3 ; i++){
        somestruct item;

        // ... fill out
       retvec.push_back(item);
}


(so essentially I avoided answering your allocation question because it doesn't apply in your case.)
Jsmith... thanks so much for your help.

I now have working example library with no memory leaks. I just don't understand how.

Is stack memory garbage collected using reference counting somehow?

In my example below I am looping calling the foo function 3,000 times.
I print out some memory addresses and I can see that memory is being re-used.
Then I changed the code to keep a vector of all result vectors and saw that memory wasn't being re-used and watched my memory go up again.
So, does C++ use garbage collection?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <iostream>
#include <vector>
#include <string>

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))

using namespace std;

typedef struct somestruct{
    string name;
    string desc;
    string something_else;
    vector < string > some_list;
} somestruct;

vector < string > foo(string a, int n){
    
    vector < string > retvec;
    for(int i = 0 ; i < n ; i++){
        string tmp(a);
        for(int j = 0 ; j < i ; j++){
            tmp.append(a);
        }
        retvec.push_back(tmp);
    }
    return retvec;
}

vector < somestruct > bar (string name, string desc, string something_else, int n){
    
    vector < somestruct > retvec;
    
    char buf[100];
    
    for(int i = 0 ; i < n ; i++){
        somestruct item;
        
        sprintf(buf, "-%d", i);
        item.name = string(name + string(buf));
        
        item.desc = string(desc + string(buf));
        
        item.something_else = string(something_else + string(buf));
        
        item.some_list.push_back(string("help")   + string(buf));
        item.some_list.push_back(string("me")     + string(buf));
        item.some_list.push_back(string("please") + string(buf));
        
        // is this correct?
        retvec.push_back(item);
    }
    
    return retvec;
}

int main(int argc, char* argv[]){
    
    char buf[100];
    
    vector < string > result1;
    vector < somestruct > result2a;
    vector < somestruct > result2b;
    vector < somestruct > result2c;
    
    result1 = foo("A", 5);
    
    cout << "result1 size : " << result1.size() << endl;
    for(int i = 0 ; i < result1.size() ; i++){
        cout << "result1[" << i << "] : '" << result1[i] << "'" << endl;
    }
    
    cout << endl;
    
    for(int i = 0 ; i < 1000 ; i++){
        result2a = bar("name a", "desc a", "something else a", 1 + i);
        result2b = bar("name b", "desc b", "something else b", 2 + i);
        result2c = bar("name c", "desc c", "something else c", 3 + i);
        // print some arbitrary memory addresses once all these indicies exist
        if(i >= 20){
            sprintf(buf, " %p %p %p %p", &(result2a), &(result2b[12].desc), &(result2c[17].some_list[1]), &(result2a[7].name));
            cout << i << buf << endl;
        }
    }
    
    cout << endl;
    
    cout << "result2a size : " << result2a.size() << endl;
    for(int i = 0 ; i < result2a.size() ; i++){
        if(i == 3){
            cout << " ..... " << endl;
            i = result2a.size() - 1;
        }
        cout << "result2a[" << i << "]" << endl;
        cout << "  " << result2a[i].name           << endl;
        cout << "  " << result2a[i].desc           << endl;
        cout << "  " << result2a[i].something_else << endl;
        for(int j = 0 ; j < result2a[i].some_list.size() ; j++){
            cout << "    " << result2a[i].some_list[j] << endl;
        }
    }
    
    cout << endl;
    
    cout << "result2b size : " << result2b.size() << endl;
    for(int i = 0 ; i < result2b.size() ; i++){
        if(i == 3){
            cout << " ..... " << endl;
            i = result2b.size() - 1;
        }
        cout << "result2b[" << i << "]" << endl;
        cout << "  " << result2b[i].name           << endl;
        cout << "  " << result2b[i].desc           << endl;
        cout << "  " << result2b[i].something_else << endl;
        for(int j = 0 ; j < result2b[i].some_list.size() ; j++){
            cout << "    " << result2b[i].some_list[j] << endl;
        }
    }
    
    cout << endl;
    
    cout << "result2c size : " << result2c.size() << endl;
    for(int i = 0 ; i < result2c.size() ; i++){
        if(i == 3){
            cout << " ..... " << endl;
            i = result2c.size() - 1;
        }
        cout << "result2c[" << i << "]" << endl;
        cout << "  " << result2c[i].name           << endl;
        cout << "  " << result2c[i].desc           << endl;
        cout << "  " << result2c[i].something_else << endl;
        for(int j = 0 ; j < result2c[i].some_list.size() ; j++){
            cout << "    " << result2c[i].some_list[j] << endl;
        }
    }
    
    return 0;
}
In your original code, using new you are allocating memory from the heap, not the stack. You were probably printing out the pointer returned by new. Since nowhere were you deleting the pointer, yes, the memory address kept growing.

Under the new implementation, item is a stack variable. Each time you reach the end of the for() loop body, item is destroyed (destructor is run), freeing the stack memory, and then the next iteration causes a new item to be constructed on the stack (at the same location, since the previous instance was destroyed).

Topic archived. No new replies allowed.