3

I wrote a simple piece of C++ code to pass addresses by reference.

I am passing the address of a variable (say y) and an array (say arr) to a class. Both arr and y will get modified inside the class. I want to have the modified values in my main().

Please find my question in the below piece of code as it is easier that way. Thanks.

#include <iostream>
using namespace std;

class A
{
public:
    // Assign  values to the array and increment _x.
    void increment()
    {
        (*_x)++;
        (*_arr)[0] = 1; // Q1. Is it safe to directly access the array like this.
        (*_arr)[1] = 2; //     Don't I have to allocate memory to the array ?
        (*_arr)[2] = 3;
    }

    // Get the address of the Variable that is passed in main. x will now have &y2.
    A (int* &arr, int* &x):
        _x(x)
    {
        *_arr = arr;
    }

private:
    int* _x;
    int** _arr;
};

int main()
{
    int y = 9;
    int arr[5];

    int *pY = &y;
    int *pArr = arr;

    A *obj1 = new A(pArr, pY);

    // This gives a compile time error. warning: initialization of non-const reference int *&' from rvalue `int *'
    // A *obj1 = new A(&y);   <-- Q2. Why does this give a Compile Time Error ?
    obj1->increment();

    cout << "y : " << y << endl;
    cout << "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << endl;
    cout << endl;
    return 0;
}
  1. In A::increment() function, I am directly assigning values to the array without allocating memory. Is it safe to do ? If not, how can I allocate memory so that I can still get the modified array values in main() ?
  2. Why do I get a compile time error whey I pass &y to A's constructor ?

Thanks in advance.

SyncMaster
  • 9,754
  • 34
  • 94
  • 137

4 Answers4

4

Question 1

In A::increment() function, I am directly assigning values to the array without allocating memory. Is it safe to do ? If not, how can I allocate memory so that I can still get the modified array values in main() ?

Answer

Yes, it is safe.

Question 2

Why do I get a compile time error whey I pass &y to A's constructor ?

Answer

&y is not an lvalue. Hence, it cannot be used where the argument type is int*&.

Problem in posted code

    *_arr = arr;

That is a problem since _arr has not been initialized to point to a valid memory. Using *_arr when _arr has not been initialized causes undefined behavior. You can change the type of _arr to:

int* _arr;

and simplify your code a little bit.

class A
{
   public:
      // Assign  values to the array and increment _x.
      void increment()
      {
         (*_x)++;
         _arr[0] = 1; // Q1. Is it safe to directly access the array like this.
         _arr[1] = 2; //     Don't I have to allocate memory to the array ?
         _arr[2] = 3;
      }

      // Get the address of the Variable that is passed in main. x will now have &y2.
      A (int* &arr, int* &x):
            _x(x),
            _arr(arr)
   {
   }

   private:
      int* _x;
      int* _arr;
};

without changing anything in main.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

This is very rarely what you want; a T** is generally an array of arrays or else a pointer value that you want to modify in the caller’s scope. However, neither seems to be what you’re doing here.

It is safe to modify *_arr[0] if and only if _arr has been initialized to a array of non-const arrays, and (*_arr)[0] if and only if it has been initialized as a pointer to a non-const array. Neither appears to be the case here, but if it is, you probably want to give the array length explicitly.

In this example, &y is a constant. You can’t modify it, so you can’t pass it as a non-const variable. You can declare a pointer int *py = &y; and pass that. But consider whether that’s what you want to do.

By the way, it’s not good style to use identifiers that start with underscores, because by the standard, they’re reserved for the compiler to use.

Davislor
  • 14,674
  • 2
  • 34
  • 49
1

You should tell us what you are trying to do. In my opinion it's nonsense using raw pointers/arrays and naked new/(missing?) delete in C++ without good reason. I would also like to note that it is not considered good practice using the _prefix for class members. Usually leading _ are used for std implementations. I recommend using m_prefix if you insist on one. And why do you give _arr the type int**? Is is supposed to be a 2D-Array? Additionally, it doesn't really make sense passing a pointer by reference. A pointer is already a pointer, if you know what I mean, just pass the pointer around.

I'm just going to assume that you are doing this to understand manual memory management or pointer arithmetics or - wait, right: Tell us what you are trying to do and why. Nevertheless, I don't understand what you have the class for:

#include <iostream>

void increment(int& x, int *arr, int sz)
{
    ++x;
    for (int i = 0; i != sz; ++i)
    {
        // this just numbers the values respectively (starting at 1)
        arr[i] = i + 1;
    }
}

int main()
{
    using namespace std;

    int y = 9;
    const int sz = 5;
    int arr[sz];

    increment(y, arr, sz);

    cout << "y : " << y << '\n'
         << "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";
}

To answer your questions: 2. First thing first: I don't see any constructor that only takes one argument.

  1. Read up on "Undefined Behaviour (UB)" starting point: What are all the common undefined behaviours that a C++ programmer should know about?

I can't repeat enough that I don't understand what you are going for and that makes it hard to give solid advice.

I tried fixing your version.. well its still terrible... I highly recommend on reading up on std::array, std::vector. Maybe on pointers, C-Style Arrays and how to pass C-Style Arrays as function arguments (note: for regular C++ programming you wouldn't be doing/using that, usually).

#include <iostream>

class A {
public:
    // Assign  values to the array and increment m_x.
    void increment()
    {
        ++(*m_x);
        m_arr[0] = 1;
        m_arr[1] = 2;
        m_arr[2] = 3;
     }

    A (int* arr, int* x):
        m_x(x), m_arr(arr)
    {
    }

private:
    int* m_x;
    int* m_arr;
};

int main()
{
    using namespace std;

    int y = 9;
    int arr[5];

    A obj1(arr, &y);
    obj1.increment();
    cout << "y : " << y << '\n'
             << "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";

    A obj2(arr, &y);
    obj2.increment();
    cout << "y : " << y << '\n'
         << "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";
}

You should also read up un pointers/references and their differences

I am actually trying to make your programming life easier. Sorry for long answer.

smoothware
  • 898
  • 7
  • 19
0

In answer to your first question

In A::increment() function, I am directly assigning values to the array without allocating memory. Is it safe to do ? If not, how can I allocate memory so that I can still get the modified array values in main() ?

you allocated memory in main(), in the line

int arr[5];

In terms of class design, you defined your class constructor to accept reference arguments, which means that an existing int* must be passed to each argument:

A (int* &arr, int* &x)

and you do so when you invoke the constructor:

A *obj1 = new A(pArr, pY);

so in this program, what you are doing is safe. A potential danger if you expect to use this class in another context would be if your arr array in main() contained fewer than 3 elements, since your increment() function initializes the third element of the array.

In answer to your second question

Why do I get a compile time error whey I pass &y to A's constructor ?

In your original constructor,

 // Get the address of the Variable that is passed in main. x will now have &y2.
 A (int* &arr, int* &x):
     _x(x)
 {
     *_arr = arr;
 }

you are dereferencing _arr before it has been initialized. One way to solve this would be to do this:

    // Get the address of the Variable that is passed in main. x will now have &y2.
    A (int* &arr, int* &x):
        _x(x)
    {
        _arr = new (int*);
        *_arr = arr;
    }

    // Destructor
    ~A ()
    {
        delete _arr;
    }

As an aside, you also use new in main(). Whenever you use new, you should also use delete to avoid a memory leak. So at the bottom of your program, before the return statement, add the following:

delete obj1;
sifferman
  • 2,955
  • 2
  • 27
  • 37