Pass array by value

B

Berk Birand

Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??

Thanks,
BB
 
A

Artie Gold

Berk said:
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??

Thanks,
BB
When an array is passed in either C or C++ (different, oft subtly
incompatible languages with a common subset), it decays into a pointer
to its first element; the pointer is passed by value.

The only way to simulate passing an array by value would be to wrap it
in a struct.

HTH,
--ag
 
P

Peter Koch Larsen

Do not use an array - use std::vector instead.
Berk Birand said:
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??
Also do not use pointers if you can avoid them.
 
E

E. Robert Tisdale

Berk said:
I am just learning about the array/pointer duality in C/C++.
I couldn't help wondering,
"Is there a way to pass an array by value?"
It seems like the only way to do is to pass it by reference?

Actually, you pass a pointer to the first element of the array.

The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}
 
B

Berk Birand

Peter said:
Do not use an array - use std::vector instead.


Also do not use pointers if you can avoid them.

Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers,
and the values are going to change. On the other hand, it's just more work!

So, what do you say about this?
 
E

E. Robert Tisdale

Berk said:
Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:

void afunc(int& i);

Or is it better to declare it as

void afunc(int* i);

I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!

Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead. I pass small objects by value

int afunct(int i);

but you can easily substitute a function declaration/definition
that passes a const reference

in afunct(const int& i);

without breaking any applications that call afunct.
You need only recompile and relink.
 
P

puzzlecracker

The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!
 
E

E. Robert Tisdale

puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a
struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}


this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. Please correct me if I am wrong!

You are wrong.

You can verify this by writing a simple test program:
cat main.cpp
#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

int main(int argc, char* argv[]) {
Array a;
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
A[j] = j;
f(a);
return 0;
}

then compiling and running it.
 
P

puzzlecracker

puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


#include <iostream>

struct Array {
int A[10];
Arrray(const Array &a)
{
if(a)
while(*A++=*a++);

else
A=0;

}

};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout<<std::endl;
 
S

Siemel Naran

Berk Birand said:
Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

If one is allowed to pass in a NULL integer (not zero, which is something),
meaning that 'i' is not supplied, then you have to choose the second
version. Otherwise prefer the first method as it makes it clear that the
caller has to pass in a valid integer.
I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers,
and the values are going to change. On the other hand, it's just more
work!

It's more work to type, not to the end program usually. Most compilers
implement references as pointers, except the pointer notation is not visible
to the end user. So both ways are on an equal footing, and which way is
better is your choice.
 
D

Dave Moore

puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) {
delete [] p_;
p_=0; sz_=0;
}
}

};

void print(Array a) { // pass by value
using std::copy;
using std::cout;

copy(a.begin(), a.end(), std::eek:stream_iterator<int>(cout," "));
cout << std::endl;
}

int main() {
Array a(10);
Array::iterator in=a.begin();

for (int i=0; in!=a.end(); ++in,++i)
*in=i;

print(a);
}
 
R

Richard Herring

In message said:
#include <iostream>

struct Array {
int A[10];
Arrray(const Array &a)
{
if(a)
while(*A++=*a++);
else
A=0;
}
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout<<std::endl;

What is that supposed to do?
Have you tried compiling it?
 
R

Richard Herring

E. Robert Tisdale said:
Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead.

Would you give the same advice if the object being modified were a
million-element vector?
 
R

Richard Herring

Dave Moore said:
puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.
#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) { Superfluous test
delete [] p_;
p_=0; sz_=0;
Why zero something that's about to be destroyed anyway?

You're living dangerously. Where's the copy assignment operator? The
compiler-generated one isn't going to work, for the same reason that you
had to provide a copy constructor and a destructor.
 
S

Shezan Baig

Berk said:
Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers,
and the values are going to change. On the other hand, it's just more work!

So, what do you say about this?

This is a question of personal preferences. I prefer to use a pointer,
because when you use the function, it is more explicit that you are
modifying the value:

SomeStruct s = { ... };
modifyingFunc(&s); // might be modified.

However, if the function does not modify, then I prefer to use const
ref for big structs/classes and just copy by value for small ones:

SomeStruct s = { ... };
nonModifyingFunc(s); // will not be modified

Again, this is just personal preference (it is not a *rule*), but I
feel it makes code easier to read especially when you don't want to
keep looking up function definitions.

Hope this helps,
-shez-
 
K

Karl Heinz Buchegger

Richard said:
Dave Moore said:
puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a
struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data.

How come?
In the posted example indeed an array is a member of the struct and not
a pointer. No further action is required. When the struct is passed per
value, a copy of the entire struct is generated and thus the array itself
gets copied.

So, Dave Moore is right
puzzlecracker is wrong
Tisdale is right (who posted that example)
 
R

Richard Herring

Karl Heinz Buchegger said:
Richard said:
Dave Moore said:
The only way to pass an array by value is to encapsulate it in a
struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data.

How come?
In the posted example indeed an array is a member of the struct and not
a pointer. No further action is required. When the struct is passed per
value, a copy of the entire struct is generated and thus the array itself
gets copied.

Yes, that's quite true. Note that you didn't use the words "static",
"dynamic" or "initialization". The point is that the compiler-generated
shallow copy will suffice for an array, but you have to write your own
deep copy for what a pointer points to.
So, Dave Moore is right

His example is (mostly) right. His explanation is wrong.
 
D

Dave Moore

Richard Herring said:
Dave Moore said:
puzzlecracker said:
The only way to pass an array by value is to encapsulate it in a
struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.

Well, perhaps my explanation would have been more clear if I had said static
and dynamic *allocation* instead of initialization. However, you can't
initialize something unless you have allocated storage for it, so my given
explanation is correct by implication.
#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) { Superfluous test
delete [] p_;
p_=0; sz_=0;
Why zero something that's about to be destroyed anyway?

These are habits I picked up before the standardization of C++, when
compilers didn't always handle destruction of base classes properly. I
spent a long time trying to figure out why the ^&(*^ I was getting
seg-faults during the destruction phase after main had finished. Putting
such superfluous checks and zero-sets seemed got rid of the errors, for
whatever reason. You are of course correct that they are probably
unnecessary with current compilers. However I haven't had to write a
container class like the above for a while .. I use STL containers now ..
much easier 8*).
You're living dangerously. Where's the copy assignment operator? The
compiler-generated one isn't going to work, for the same reason that you
had to provide a copy constructor and a destructor.

Point taken .. omitting the copy assignment operator was an oversight ... I
was just trying to come up with a quick illustration to help alleviate
puzzlecracker's confusion.

[snip]

Dave Moore
 
D

David Harmon

On Thu, 03 Feb 2005 23:48:25 -0500 in comp.lang.c++, Berk Birand
I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value?

Not directly, no.
It seems like the only way to do is to pass it by reference??

The assumption in C was that it would be a horrible thing to copy
arrays in order to call functions. Too big, too expensive.

Of course in C++ you will typically be using std::vector instead of
bare naked arrays for ordinary application purposes. So, the task
becomes that of being careful to pass by reference, and not by value
requiring a copy.
 
R

Richard Herring

Dave Moore said:
Richard Herring said:
Dave Moore said:
The only way to pass an array by value is to encapsulate it in a
struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.

Well, perhaps my explanation would have been more clear if I had said static
and dynamic *allocation* instead of initialization. However, you can't
initialize something unless you have allocated storage for it, so my given
explanation is correct by implication.

Not really. What I'm objecting to is the words "static" and "dynamic",
whatever it is that they describe. Not all use of pointers implies
dynamic allocation. Objects in automatic storage aren't static.

The real issue is the difference between shallow and deep copying, and
the circumstances (ownership of a resource that lies outside the object)
that require the latter.

[snip]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top