Buck
Buck

Reputation: 13

Passing arrays of pointers in C++

Not sure what I'm doing wrong here. I'm trying to add a new element and up until the function ends it appears to have worked. But once I try to access anything in it after I get a segmentation fault.

This is just sample code I'm using to try and figure out what I'm doing wrong, with console output to help determine current values.

#include <iomanip>
#include <iostream>

using namespace std;


class foo
    {
    private:
    int z;
    public:
    foo(int);
    int getz();
    void setz(int);
    };

    foo::foo(int zz)
    {
        z = zz;
    }

    int foo::getz()
    {
        return z;
    }

    void foo::setz(int zz)
    {
        z = zz;
    }

void boo(foo** x, unsigned* n)
{
    foo** b = new foo*[3];

    for (int i=0; i<2; i++)
        b[i] = x[i];
    b[2] = new foo(5);
    cout << x[0]->getz() << x[1]->getz();
    delete[]x;
    x = b;
    cout << x[0]->getz() << ' ' << x[1]->getz() << ' ' << x[2]->getz();
    b = nullptr;
    (*n)++;
}

int main() {
    
    foo** a = new foo*[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(a, &s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}

Upvotes: 0

Views: 70

Answers (1)

A M
A M

Reputation: 15277

The problem is, how you pass your array to your function "boo".

As a general notice, please let me recap how we can use in/out parameters for functions. As you may have heard, we can

  • pass by value. A copy of the parameter will be made and the all assignmnets to such a variable, will not be available after the function returns.
  • pass by pointer. In the called function the the pointers will be dereferenced. Basically this is also pass by value. But here the address will be passed. And with dereferencing the address, the original value can be modified.
  • pass by reference (advised method). A reference to the original variable will be passed to the function and evrything can be handled as if interacting directly with the original variable.

You are passing the foo** as value. That is the reason why it will not work. Delete the parameter will work, but the assignment of x to b will not be visible to the outside world. And after the function "boo" returns, a will be deleted.

Let us look at the good all "swap" example.

// Call by value, will not work. "a" will be changed, but it is a copy
void swapv(int a, int b) {
    int temp = a;
    a = b;
    b = tmp;
}
// Call by address(via pointer) will work. Dereferencing is necessary
void swapp(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = tmp;
}
// Call by reference. Recommended solution. Will simply work with same variable names
void swapr(int &a, int &b) {
    int temp = a;
    a = b;
    b = tmp;
}

As you can see. The passing by reference approach is most simple.

Again, what you do is passing "foo**" by value. By the way, "n" is passed via pointer. If you use a typedef or usingstatement, it will get very obvious.

Look at the following piece of code using a using-alias statement.

using fooPtrPtr = foo**;
using fooPtr = foo*;

void boo(fooPtrPtr x, unsigned* n)
{
    fooPtrPtr b = new fooPtr[3];

    for (int i = 0; i < 2; i++)
        b[i] = x[i];
    b[2] = new foo(5);
    cout << x[0]->getz() << x[1]->getz();
    delete[]x;
    x = b;
    cout << x[0]->getz() << ' ' << x[1]->getz() << ' ' << x[2]->getz();
    b = nullptr;
    (*n)++;
}
int main() {

    fooPtrPtr a = new fooPtr[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(a, &s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}

Now you can immediately see the problem. You see immediately that "x" is passed by value to "boo".


But this leads then directly to the recommended and easy solution. Pass it by reference. Like the below:

using fooPtrPtr = foo**;
using fooPtr = foo*;

void boo(fooPtrPtr& x, unsigned& n)
{
    fooPtrPtr b = new fooPtr[3];

    for (int i = 0; i < 2; i++)
        b[i] = x[i];
    b[2] = new foo(5);
    cout << x[0]->getz() << x[1]->getz();
    delete[]x;
    x = b;
    cout << x[0]->getz() << ' ' << x[1]->getz() << ' ' << x[2]->getz();
    b = nullptr;
    n++;
}
int main() {

    fooPtrPtr a = new fooPtr[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(a, s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}

With pointers and pointers to pointers, using or typedef will be very helpful.

Very simple. And without using using the code will be not so easy to understand and could look like the below.

One ampersand "&" after foo** solves the whole problem.

void boo(foo**& x, unsigned& n)
{
    foo** b = new foo*[3];

    for (int i = 0; i < 2; i++)
        b[i] = x[i];
    b[2] = new foo(5);
    cout << x[0]->getz() << x[1]->getz();
    delete[]x;
    x = b;
    cout << x[0]->getz() << ' ' << x[1]->getz() << ' ' << x[2]->getz();
    b = nullptr;
    n++;
}
int main() {

    foo** a = new foo*[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(a, s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}


And last but not least also the version with pass by address / via pointer. Then a lot of dereferencing is necessary, which makes code even harder to read:

void boo(foo*** x, unsigned *n)
{
    foo** b = new foo*[3];

    for (int i = 0; i < 2; i++)
        b[i] = (*x)[i];
    b[2] = new foo(5);
    cout << (*x)[0]->getz() << (*x)[1]->getz();
    delete[](*x);
    *x = b;
    cout << (*x)[0]->getz() << ' ' << (*x)[1]->getz() << ' ' << (*x)[2]->getz();
    b = nullptr;
    (*n)++;
}
int main() {

    foo** a = new foo*[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(&a, &s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}

We can also use aliases to make this a little more readable, but still clumsy.

using fooPtrPtr = foo**;
using fooPtr = foo*;

void boo(fooPtrPtr* x, unsigned *n)
{
    fooPtrPtr b = new fooPtr [3];

    for (int i = 0; i < 2; i++)
        b[i] = (*x)[i];
    b[2] = new foo(5);
    cout << (*x)[0]->getz() << (*x)[1]->getz();
    delete[](*x);
    *x = b;
    cout << (*x)[0]->getz() << ' ' << (*x)[1]->getz() << ' ' << (*x)[2]->getz();
    b = nullptr;
    (*n)++;
}
int main() {

    fooPtrPtr a = new fooPtr[2];
    unsigned s = 2;

    a[0] = new foo(1);
    a[1] = new foo(2);
    boo(&a, &s);
    cout << s;
    cout << a[0]->getz();
    return 0;
}

Upvotes: 1

Related Questions