passing char[][] by reference

Pages: 12
I keep getting errors trying to pass a dual array of char[][] by reference.
1
2
void foo(char& **arr) { }
void foo(char& arr[][]) { }


Neither of the above seem to work. In eclipse I get a syntax error before even compiling with the first, and with the second declaration I get error:

error: declaration of 'puzArr' as array of references 


This will be a dynamic array, I figured passing by reference would be fine even though I'm not planning on changing the array at all, there is however one of my functions where I do need to change another array so reference will be necessary.
Last edited on
http://www.cplusplus.com/forum/articles/17108/
All the advice I have. (And it's really not even mine.) Try removing one of the subscript/dereference operators (since in this case, they are effectively the same thing) and see what happens.
Last edited on
Never use the '&' symbol when passing arrays. Arrays are not objects that can be passed by reference. Passing arrays to functions is misleading in C++ because the NOOB will think that they are passing something by value when in reality you are always passing the function a pointer to the array. If foo modifies the array the caller of foo will notice the changes after foo returns. There is no point in passing an array by reference.
Ahh thanks, I forgot that post was there, and then I was also trying to find a post (I think chris wrote) about passing by value vs passing by reference. I was sure I had it bookmarked. :(

Looks like I'll have to use a mimic 1D array.

 
void foo(char arr[][]){}

If foo modifies the array the caller of foo will notice the changes after foo returns.

Weird. That makes no sense to me, why wouldn't it just be a copy of the array? and does that work for both dynamic and non-dynamic dual arrays?
Last edited on
You could always write a wrapper class for a 2-d array that's secretly a 1-d mimic of a 2d array.
Yeah I had a look at the class disch wrote, mine would have to be pretty specific and therefor wouldn't be re-usable, NO STL functions.
Never use the '&' symbol when passing arrays. Arrays are not objects that can be passed by reference


Not true.

You can pass an array by reference. One of the benefits to doing so is that it ensures the array is of a specific size.

The downside is that you can't pass dynamically allocated arrays to said function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void func(int (&v)[5])
{
  // 'v' is an int[5] array
  // passed by reference

  // you can be sure that v has exactly 5 elements in this function
  // additionally.. sizeof(v) will be sizeof(int)*5
}

int main()
{
  int a[5];
  func(a);  // works

  int b[6];
  func(b);  // fails

  int* c = new int[5];
  func(c);  // fails

  return 0;
}


You might also notice that this requires you specify the exact dimensions of the array.

Of course, this is hardly ever used because it's so obscure. I just felt like being hypertechnical.

Weird. That makes no sense to me, why wouldn't it just be a copy of the array?


C++ simply does not allow arrays to be passed by value. There's no way to do it unless the array is in some other kind of container/struct/class that can be copied.

I agree it's a little funky. But it's probably a good thing because it lets you do things like this to make your code more clear:

1
2
3
4
5
6
7
8
9
void MyFunc( int foo[5] )
{
  // this is the same as:
  //  void MyFunc( int* foo )

  // however the above is more clear because it instructs the user to pass an
  // array with no less than 5 elements, whereas the lower definition doesn't
  //  specify.
}


does that work for both dynamic and non-dynamic dual arrays?


If by dual arrays you mean 2D arrays, you cannot write any function that takes both a dynamic and non-dynamic 2D array as a parameter.

"Straight" 2D arrays are more like 1D arrays with different syntax (they're actually arrays of arrays)

whereas "nested new" (dynamic) 2D arrays are arrays of pointers to other arrays.

They are therefore two entirely different types and cannot be interchanged.

Hence one reason why MD arrays are evil.

I suggest you skim the doc tummychow linked to.
I though arrays were always passed by reference regardless?
there's a very subtle difference between "by reference" and "by pointer"

very, very subtle.
yeah...I think that's why I was having such a hard time learning pointers. Do you care to explain it to us newbies?
@yoked88 I thought I had saved it somewhere, not bookmarked, copied to textfile:

chrisname wrote:
Passing by value, by pointer-to-const, and by const-reference all prevent the caller's actual
parameter from being changed by the function. Passing by pointer (to non-const) and by
(non-const) reference allows the function to change the caller's actual parameter.

Passing by value makes a copy of the caller's actual parameter onto the stack.
References are typically implemented in the compiler as pointers, so passing by reference
and by pointer both store a pointer onto the stack, which is very cheap.

From a performance perspective, pointers and references are equivalent. However, pointers
can be NULL whereas references cannot. A well-implemented function that takes a pointer as
parameter must check the pointer for NULL before dereferencing, and then deal with the
possibility of NULL (often times, but not always, an error case). A well-implemented function
that takes a reference as parameter need not check the reference for NULL since it cannot be,
so the error case is avoided. But accessing the data "referred to" by a pointer or a reference
requires one extra memory access than if passed by value.

From an implementation perspective, accessing the data "referred to" by the pointer requires
the dereference operator (*). Accessin the data "referred to" by a reference is the same as
if the parameter were passed by value. Examples, to illustrate:


int by_value( int x ) { return x + 5; }
int by_pointer( const int* x ) { return x == NULL ? 0 : *x + 5; } // Returns 0 if x == NULL or *x == -5...
int by_reference( const int& x ) { return x + 5; } // Same as by_value case!


In general, pass by value for easy-to-copy types and you don't need to change the caller's
actual parameter. If you don't need to change the caller's actual parameter but it is expensive
to copy onto the stack, use const references. If you do need to change the caller's actual
parameter, use a non-const reference. You should use pointers only when NULL is not an
error case for your function.
pointers and references are similar in that they both accomplish the same thing.

However they are different conceptually:

- A pointer is a seperate variable which "points to" (tells you where to find) another variable.

- A reference is not a seperate variable, but is instead the very same variable. References don't "point to" something... they are that something.

When passing X to a function, passing it by pointer is like giving the function the location in which it can find X.
When passing by value, it makes a copy of X and gives the copy to the function.
When passing X by reference, it's like actually giving X to the function.


So... how does this apply to arrays.

Well arrays and pointers are tricky. Arrays have multiple elements, each element lying immediately after the previous one in memory. So if you have char myarray[100];, and if 'myarray[0]' is at address "1000", then 'myarray[1]' would be at address 1001.

Knowing this... you can pass an array by pointer to a function by giving it a pointer to the very first element in the array. Because if you know where to find the first element, you know that all the other elements are immediately after it. So when you have something like this:

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
void func(int*);

void foo()
{
    int myarray[5];
    func( myarray );  // give 'func' a pointer to the first element in myarray
//  func( &myarray[0] ); // this is an equivilent / alternative way to do the exact same thing
}

void func(int* ptr)
{
    // here, ptr == &myarray[0]
    //  so when we do this:
    *ptr = 1;  // this is like doing myarray[0] = 1;

    // and because we know that the next element in the array is at
    // the address immediately after ... we can just add 1 to the
    // pointer to get the next element:
    *(ptr + 1) = 2;  // this is like doing myarray[1] = 2;

    // now the [bracket] operators are shorthand for the * operator.
    //  so these lines would be the same:
    ptr[0] = 1;  // is exactly the same as *ptr = 1;
    ptr[1] = 2;  // is exactly the same as *(ptr + 1) = 2;

    // now because 'ptr' is a pointer to the first element, if we change the pointer,
    // we change what it points to.
    ++ptr;  // increment the pointer

    // now, ptr points to &myarray[1].  So this:
    ptr[0] = 3;  // is like saying myarray[1] = 3;
}


The concept of a reference is much simpler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void func(int (&ref)[5]);

void foo()
{
    int myarray[5];
    func( myarray );  // pass the array by reference to the function
//  func( &myarray[0] ); // this is NOT the same thing anymore, because it is a pointer
       //  to the first element, and we're no longer giving a pointer to the first element, but
       //   instead are giving the array itself.
}

void func(int (&ref)[5])
{
    // here, ref doesn't point to myarray.  ref IS myarray.
    // anything you do to 'ref' is just like you were doing it to myarray.  They are one and the same.

    ref[0] = 1;  // same as myarray[0] = 1;
    ref[1] = 2;  // same as myarray[1] = 2;

    // and unlike pointers, 'ref' cannot be changed to point to something else
    ++ref;  // ERROR  this would be like doing ++myarray; which of course makes absolutly no sense.
}



EDIT: doh, too slow. Oh well this is still an informative post.
Last edited on
Definitely your post is specifically talking about arrays.

This is stuff I have all read before, I find it annoying I never learn by reading, it's not until I have to "DO" something that I actually learn by failing and doing it. I wish I could learn by reading but it just doesn't seem to happen all that often.... occasionally I get lucky.
Last edited on
Thank you lots Disch and gcampton! That is tons of info to process, I really appreciate it.
Would this work? Looks ok to me, although I'm not too sure of lines 42,43
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
#ifndef ARRAY2D_H_
#define ARRAY2D_H_

class Array2D
{
public:
  // constructor
  Array2D(unsigned wd,unsigned ht)
    : nWd(wd), nHt(ht), pAr(0)
  {
    if(wd > 0 && ht > 0)
      pAr = new char[wd * ht];
  }

  // destructor
  ~Array2D()
  {
    delete[] pAr;
  }

  // indexing (parenthesis operator)
  //  two of them (for const correctness)

  const Array2D& operator () (unsigned x,unsigned y) const
  {  return pAr[ y*nWd + x ];   }

  Array2D& operator () (unsigned x,unsigned y)
  {  return pAr[ y*nWd + x ];   }

  // get dims
  unsigned GetWd() const { return nWd; }
  unsigned GetHt() const { return nHt; }


  // private data members
private:
  unsigned nWd;
  unsigned nHt;
  char* pAr;

  // to prevent unwanted copying:  not sure if this is right
  Array2D(const Array2D&);
  Array2D& operator = (const Array2D&);
};


I get errors:

line 25: invalid initialization of reference of type 'const Array2D&' from expression of type 'char'
line 28: declaration of 'operator()' as non-function 
Last edited on
lol That's out of my leage at the moment, sorry. Next chapter is operator overloading which means in like 3 weeks for me.
nice discussion guys :D

how about discussing 2d arrays, and pointers; i mean

int main(int argc, char** argv);
vs
int main(int argc, char* argv[]);

if you know other possible combinations of * and [], please discuss it here; im learning from you guys...

thanks
Well since they are passed by pointer/reference....the same discussion applies no?
gcampton: your () operators should be returning char& for the nonconst version --- and char or const char& for the const version. You shouldn't be returning Array2D&.

Other than that it looks good.

And lines 42,43 are there to prevent unwanted array copying. Copy ctor and assignment are private with no body, so they cannot be called without error, making it impossible to copy an array.

If you want, you can implement a copy ctor and assignment operator, but then accidental (potentially expensive) copies are possible.

Also note that the default copy/assignment cannot be used for this class because you have a dynamically allocated pointer. You'd have to write custom functions which:

- allocate a new buffer of the required size (assignment operator may also have to delete[] the existing buffer)
- copy all elements to that new buffer


----------
blackcoder:

both of those are exactly the same. argv is a pointer to an array of char pointers in both instances.
Last edited on
ah yep, thanks:
1
2
3
4
const char& operator() (unsigned x,unsigned y) const
{    return pAr[ y*nWd + x ];    }
char& operator() (unsigned x,unsigned y)
{    return pAr[ y*nWd + x ];    }
Last edited on
Pages: 12