Sign in to follow this  
Alpha Brain

passing a struct array in a function as refence... ?

Recommended Posts

#include <iostream>
#include <string>

using namespace std;

struct COORD
{
	int x;
	int y;
};

void set_zero(COORD &n[])
{
	for(int i=0; i<5; i++)
	{
		n[i].x = 0;
		n[i].y = 0;
	}
}

void main(void)
{
	COORD c[5];

	for(int i=0; i<5; i++)
	{
		c[i].x = 10;
		c[i].y = 10;
	}

	set_zero(c);
}

error: error C2234: '<Unknown>' : arrays of references are illegal it seems that arrays of references are illegal... Then what is the possible way to pass an array of data with references or pointers? Thanks!

Share this post


Link to post
Share on other sites
It's mainly an operator priority thing (and also, you're missing the array size: arrays in C have a fixed size that is embedded in the type).

void set_zero(int (&n)[5]) {}

Share this post


Link to post
Share on other sites
(me again)

mmmm even if the syntax is okay for the compilor, the values isn't returning from the parameter...


#include <iostream>
#include <string>

using namespace std;

struct COORD
{
int x;
int y;
};

void set_zero(COORD (&n)[5])
{
for(int i=0; i<5; i++)
{
n[i].x = 0;
n[i].y = 0;
}
}

void main(void)
{
COORD c[5];

for(int i=0; i<5; i++)
{
c[i].x = 10;
c[i].y = 10;

cout << "c[" << i << "].x = " << c[i].x << endl;
cout << "c[" << i << "].y = " << c[i].x << endl << endl;
}

cout << "---------------------------------------------------------" << endl;
cout << "CALL FUNCTION TO SET ALL COORDS FROM ARRAY TO ZERO " << endl;
cout << "---------------------------------------------------------" << endl;

set_zero(c);

// test to see if coords are really to zero.

for(i=0; i<5; i++)
{
c[i].x = 10;
c[i].y = 10;

cout << "c[" << i << "].x = " << c[i].x << endl;
cout << "c[" << i << "].y = " << c[i].x << endl << endl;
}
}

Share this post


Link to post
Share on other sites
http://www.cppreference.com/operator_precedence.html

here you can find the info you want.

Alternatively you could just pass a COORD* pointer. The name of an array is a pointer to it's first element, so you can do this:


#include <iostream>

void print(int *array, int size)
{
std::cout << array[13] << std::endl;
};

int main()
{
int array[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

print(array, 14);
};






EDIT: you actually reset the values to 10 after calling set_zero and before reprinting them :P Careful when copy-pasting :P

Share this post


Link to post
Share on other sites
In this example, there is no need to pass the array by reference. Don't forget that arrays in C are really *just* pointers (extra syntax non-withstanding), so any function passed an array can modify the array's contents.
You only need to pass a reference to an array if you need the function to change the array pointer itself (i.e. pointing it at another buffer), and that doesn't work for static arrays.

Share this post


Link to post
Share on other sites
anyone could take my code please and bring changes to it? would really help me to understand by seeing the difference...

EDIT: AHHHHHHHHHHHHHHHHHHH! I'm stupid! really stupid!!!

lol

I've reassigned the value to 10 again after calling the set_zero function.

it's what we call "danger of copy/pasting code" lol

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
In this example, there is no need to pass the array by reference. Don't forget that arrays in C are really *just* pointers (extra syntax non-withstanding), so any function passed an array can modify the array's contents.
You only need to pass a reference to an array if you need the function to change the array pointer itself (i.e. pointing it at another buffer), and that doesn't work for static arrays.


Arrays have size information while pointers don't (you cannot determine how many elements a pointer can iterate through). Arrays decay to a pointer to their first element when evaluated in a pointer context, which loses their size information. Passing an array by reference conserves the type information. You cannot change what the array represents (an array is a sequence of elements, it doesn't point to one). Dynamic arrays are generally an incorrect or approximate denomination, the only arrays in C and C++ are static-sized ones, the rest is either pointer to buffers or container classes.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
In this example, there is no need to pass the array by reference. Don't forget that arrays in C are really *just* pointers (extra syntax non-withstanding), so any function passed an array can modify the array's contents.


Arrays decay to pointers, arrays are not pointers.

Quote:

You only need to pass a reference to an array if you need the function to change the array pointer itself (i.e. pointing it at another buffer), and that doesn't work for static arrays.


If you pass a reference to an array, you can use sizeof on the array and not get sizeof(pointer).

However the OP might find he is better served by using std::vector or boost::array.

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
Quote:
Original post by swiftcoder
In this example, there is no need to pass the array by reference. Don't forget that arrays in C are really *just* pointers (extra syntax non-withstanding), so any function passed an array can modify the array's contents.

Arrays decay to pointers, arrays are not pointers.

nitpicker :)
If I declare 'char A[5]', A is a pointer. Albeit to a either the static data section or to a region on the stack, and immutable.

Quote:
Quote:
You only need to pass a reference to an array if you need the function to change the array pointer itself (i.e. pointing it at another buffer), and that doesn't work for static arrays.

If you pass a reference to an array, you can use sizeof on the array and not get sizeof(pointer).

But in fact that is just a syntactical issue, and you *have* created an unnecessary temporary pointer. And are you quite sure about that? I am not near a compiler to test that, but it seems to me you should get the same result whether you pass an array, or a reference to an array (note that I never mentioned using a pointer directly).

Quote:
However the OP might find he is better served by using std::vector or boost::array.

Most likely.

Share this post


Link to post
Share on other sites
It looks as if several posters may have confused the terms 'array', 'pointer', and 'reference' in this context.

// 1) This function takes an array as an argument
void funcArray(char arg[5]);
// 2) This one takes a pointer
void funcPointer(char *arg);
// 3) And this on etakes a *reference* to an array
void funcRefArray(char (&arg)[]);
// 4) and finally... this one takes a reference to a pointer
void funcRefPointer(char *(&arg));


I never mentioned the pointer variants, I was talking about 1) and 3), and pointing out that in either the fnction can modify the array contents.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Quote:
Original post by rip-off
Quote:
Original post by swiftcoder
In this example, there is no need to pass the array by reference. Don't forget that arrays in C are really *just* pointers (extra syntax non-withstanding), so any function passed an array can modify the array's contents.

Arrays decay to pointers, arrays are not pointers.

nitpicker :)
If I declare 'char A[5]', A is a pointer. Albeit to a either the static data section or to a region on the stack, and immutable.


Nope. A is a C++ symbol. When the compiler translates the code to an exectable format "A" no longer exists. Its value is an array. &A == &A[0]. With a pointer &Pointer != &Pointer[0]. sizeof(A) == Num bytes in array. sizeof(Pointer) == the size of an address on the targer platform. A cannot be reseated, Pointer could be made point to some other variable.

The fact that arrays decay to pointers means that many properties of pointers are conceptually shared by arrays but they are not the same.

Quote:
Quote:
Quote:
You only need to pass a reference to an array if you need the function to change the array pointer itself (i.e. pointing it at another buffer), and that doesn't work for static arrays.

If you pass a reference to an array, you can use sizeof on the array and not get sizeof(pointer).

But in fact that is just a syntactical issue, and you *have* created an unnecessary temporary pointer. And are you quite sure about that? I am not near a compiler to test that, but it seems to me you should get the same result whether you pass an array, or a reference to an array (note that I never mentioned using a pointer directly).

I see no temporary pointer. Maybe at underlying levels the compiler will choose to use this to implement the code, but we are not writing code at that level.
I believe you are incorrect, unless I'm misinterpreting you. You will get different results.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
If I declare 'char A[5]', A is a pointer. Albeit to a either the static data section or to a region on the stack, and immutable.


No.

The address of an array variable is the address of its contents. This is unlike pointers, the address of which is irrelevant, but the value of which contains the address of something else.

The sequence of operations performed for accessing A[3] is:
  • Get the address of A, add 3, dereference (if A is an array).
  • Get the address of A, dereference, add 3, dereference (if A is a pointer).


That is, accessing a memory buffer through a pointer requires an additional dereference in order to access the value of the pointer, which is the address of the buffer.

Quote:
But in fact that is just a syntactical issue, and you *have* created an unnecessary temporary pointer. And are you quite sure about that? I am not near a compiler to test that, but it seems to me you should get the same result whether you pass an array, or a reference to an array (note that I never mentioned using a pointer directly).


When you pass a reference to an array, the type is conserved. Since the array size is encoded in the type, it is accessible to the compiler. On the other hand, you cannot pass an array by value in C and C++. The array automatically decays to a pointer if you do, and the size information is lost. The only way to conserve type information is using a reference:


#include <iostream>

int byref(int (&a)[5])
{
return sizeof(a);
}

int byval(int a[5])
{
return sizeof(a);
}

int main()
{
int a[5];
std::cout << byref(a) << std::endl << byval(a) << std::endl;
}


Outputs:
20
4


Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
It looks as if several posters may have confused the terms 'array', 'pointer', and 'reference' in this context.
*** Source Snippet Removed ***
I never mentioned the pointer variants, I was talking about 1) and 3), and pointing out that in either the fnction can modify the array contents.


This may be true, but when passing an array to a function declared using (1) arg *is* a pointer, this is due to C++ specifying that an array passed as an argument to a function will decay to a pointer. This is a singular point of confusion.


void example( char arg[] );

void foo() {
char array[4] = {};
example(array); // array decays to a pointer
char *pointer = array;
example(pointer); // look we can pass a pointer directly
// this wont
// array++;

}

void example( char arg[] ) {
// look what we can do now!
arg++;
}



However, 99% of the time passing an array by reference will be the same as passing it by pointer decay, as it is rare for someone to use "array specific" things like the sizeof trick.

An useful example that can be achieved via reference but not via pointer decay is an array size template:

template< class T, int N >
int array_size( T (&arg)[N] ) {
return N;
}

void bar( int arg[] );

void foo() {
int array[5];
int x = array_size(array); // x == 5
bar(array); // wont work because...
}

void bar( int arg[] ) {
int x = array_size(arg); // ...no function to match array_size( int *& )
}

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
The sequence of operations performed for accessing A[3] is:
  • Get the address of A, add 3, dereference (if A is an array).
  • Get the address of A, dereference, add 3, dereference (if A is a pointer).


Huh? Consider the functions:

int foo(int A[], int n) {
return A[n];
}
int bar(int * A, int n) {
return A[n];
}

Here's the assembly MSVC 2003 generates for foo:

mov eax, DWORD PTR _n$[esp-4]
mov ecx, DWORD PTR _A$[esp-4]
mov eax, DWORD PTR [ecx+eax*4]
ret 0

Here's bar:

mov eax, DWORD PTR _n$[esp-4]
mov ecx, DWORD PTR _A$[esp-4]
mov eax, DWORD PTR [ecx+eax*4]
ret 0

Share this post


Link to post
Share on other sites
this thread have moved beyond the scope of my knowledge hehe
ok another question that may sound stupid to you;


#include <iostream>
#include <string>

using namespace std;

void alter(string names[])
{
names[0] = "zombie";
names[1] = "vampire";
names[2] = "mummy";
names[3] = "werewolf";
names[4] = "grim reaper";
}

void main(void)
{
string names[] = {"imp", "critter", "slime", "goblin", "troll"};
int i=0;

for(i=0;i<5;i++)
{
cout << names[i] << endl;
}

cout << endl;

alter(names);

for(i=0;i<5;i++)
{
cout << names[i] << endl;
}
}


Now what do I do if I don't want the argument to be affected by the values inside the function? Since arrays seems to act like pointer, it's a tricky question for a beginner...

Share this post


Link to post
Share on other sites
First, main returns an int, not void. The C++ Standard even specifies that you don't even need to write return in main, it will return 0 unless you explicitly return a different value.

Use std::vector. Its better than an array as it acts how you expect when you pass it to a function or return it from one:


#include <iostream>
#include <vector>
#include <string>

using namespace std;

// turns out I find a use for this example below =)
template< class T, int N >
int array_size( T (&arg)[N] ) { return N; }

// because we didnt pass the vector by reference, the
// vector in main will not be affected
void alter(std::vector<std::string> names)
{
names[0] = "zombie";

names[1] = "vampire";

names[2] = "mummy";

names[3] = "werewolf";

names[4] = "grim reaper";
}

int main()
{
const char *names_data[] = {"imp", "critter", "slime", "goblin", "troll"};

// there is no easy way to initialise a std::vector directly
// currently this is about as easy as it gets
// in practise though such data will be loaded from
// an external source anyway
std::vector<std::string> names(data, data + array_size(data);

// declare loop variables inside the for loop
// its much clearer
//int i=0;

for( int i = 0 ; i < names.size() ; i++ )
{
cout << names[i] << endl;
}

cout << endl;

alter(names);

for( int i = 0 ; i < names.size() ; i++ )
{
cout << names[i] << endl;
}
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Alpha Brain
but without the vector, it isn't possible? (just want to understand the basic way first)

You would have to allocate a new array, and copy the contents. C/C++ arrays can not be passed by value, so no matter how much you pass the 'handle' (basically a pointer) to the array around, each will refer to the same array.

Share this post


Link to post
Share on other sites
I think that this thread is a very strong argument for the use of std::vector instead of arrays. Arrays are far too complicated to be considered a basic language feature. I avoid their use as much as possible. When I do use an array it is either a local variable or I new[] some memory for use with a pointer. I would never write a function that takes an array as a parameter.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Huh? Consider the functions:

int foo(int A[], int n) {
return A[n];
}
int bar(int * A, int n) {
return A[n];
}


Your two functions are identical: A is a pointer in both cases. I suspect that passing an array by reference will pobably result in the same thing, since there is an additional indirection.

What would be interesting is testing the cases with an array and a pointer both on the stack (although the pointer will probably end up in a register), or a pointer and an array both being global, or an array and a pointer both members of a structure.

Share this post


Link to post
Share on other sites
Quote:
Original post by Alpha Brain
but without the vector, it isn't possible? (just want to understand the basic way first)


Well, arrays are the old way, not necessarily the basic way. An std::vector is a normal C++ object which behaves as one would expect at first glance (comparison, copy, assignment, size, pass-by-value, type definitions). By contrast, using C arrays would require that you actually learn new syntax and semantics that are unlike anything else in C++.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Your two functions are identical: A is a pointer in both cases. I suspect that passing an array by reference will pobably result in the same thing, since there is an additional indirection.

No they aren't. In foo A is a reference to an array. In bar A is a pointer. In fact, under C++ overloading rules you could even have

int foo(int A[], int n);
int foo(int * A, int n);

and the compiler will be happy as a clam. However, in both cases you'll note the complete lack of the extra dereference that you claim happens with a pointer.

Quote:
What would be interesting is testing the cases with an array and a pointer both on the stack (although the pointer will probably end up in a register), or a pointer and an array both being global, or an array and a pointer both members of a structure.

You have a compiler. Try it. You still won't see that extra dereference.

Share this post


Link to post
Share on other sites
If you really want *an array* (i.e. you have a specific size in mind for it, for a good reason, which you know ahead of time, and don't want to change during the program execution), but you also want it to behave like an object (i.e. none of this pointer-decay nonsense - if you pass it by value, it gets passed by value), then what you want is boost::array.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
No they aren't. In foo A is a reference to an array. In bar A is a pointer. In fact, under C++ overloading rules you could even have

int foo(int A[], int n);
int foo(int * A, int n);

and the compiler will be happy as a clam.


#include <iostream>

int fun(int a[])
{ // <--- line 4
return sizeof(a);
}

int fun(int *a)
{ // <--- line 9
return sizeof(a);
}

int main()
{
int a[5];
int *b = a;
std::cout << fun(a) << fun(b) << std::endl;
}


g++ 3.4.0 (-W -Wall -ansi -pedantic) replies:
test.cpp: In function `int fun(int*)':
test.cpp:9: error: redefinition of `int fun(int*)'
test.cpp:4: error: `int fun(int*)' previously defined here


Visual C++ 2005 Express Edition (/W4 /Za) replies:
test.cpp(9) : error C2084: function 'int fun(int [])' already has a body
test.cpp(3) : see previous definition of 'fun'


Besides, even if C++ did implement this as a pass-by-reference, you would still have problems with C compatibility (in C, the two definitions above are Equivalent). Last but not least, the int a[] version behaves like a pointer, but not like an array: it has the same size (sizeof(int*)) regardless of the number of elements, it's an lvalue, and it cannot be passed by reference to a function which expects an array to be passed by reference.

Tested on both g++ and Visual C++ (the above versions).
#include <iostream>

int arr1(int a[])
{
return sizeof(a); // 4
}

int arr2(int (&a)[5])
{
return sizeof(a); // 20
}

int main()
{
int a[5];
std::cout << arr1(a) << std::endl << arr2(a) << std::endl;
}



Quote:
However, in both cases you'll note the complete lack of the extra dereference that you claim happens with a pointer.


The line in bold below (from your provided listing) dereferences stack address esp-4 and loads its contents into register ecx.
	mov	eax, DWORD PTR _n$[esp-4]
mov ecx, DWORD PTR _A$[esp-4]
mov eax, DWORD PTR [ecx+eax*4]
ret 0


Let us compare this with a naive stack-based array:
int arr(int N) {
int a[5];
return a[N];
}

mov eax, DWORD PTR _N$[esp-4]
mov eax, DWORD PTR _a$[esp+eax*4]
ret 0


The above uses the base address of a (which is esp) instead of loading the value of the pointer into ecx. One could argue that the pointer was passed by argument, while the array was present on the stack. It's true: you cannot compare an array to a pointer passed by argument, because the two involve different mechanisms.

Quote:
You have a compiler. Try it. You still won't see that extra dereference.


I didn't have a compiler this morning, and was on my way to work anyway. Now that I'm here, let's get started. I am using VC++ 2005 EE for generating the listings, with optimization /O2.

First, a comparison of the array and pointer being part of an object. I give the corresponding assembly listings as comments in the code.
struct st
{
int *b;
int a[5];
};

int arr(st & s, int N) {
return s.a[N];
// mov eax, DWORD PTR _N$[esp-4]
// mov ecx, DWORD PTR _s$[esp-4]
// mov eax, DWORD PTR [ecx+eax*4+4]
}

int ptr(st & s, int N) {
return s.b[N];
// mov eax, DWORD PTR _s$[esp-4]
// mov ecx, DWORD PTR [eax] <-- this one
// mov edx, DWORD PTR _N$[esp-4]
// mov eax, DWORD PTR [ecx+edx*4]
}


As visible above, there is an additional "load contents of pointer into register" step when using a pointer, whereas the array version simply uses the base array address to access the member.

This still happens if the structure is passed by value (which only removes the initial loading of the address of s from esp-4).

Now, using a global pointer and a global array:
int a[5] = { 1, 2, 3, 4, 5 };
int * b = a;

int arr(int N) {
return a[N];
// mov eax, DWORD PTR _N$[esp-4]
// mov eax, DWORD PTR ?a@@3PAHA[eax*4]
}

int ptr(int N) {
return b[N];
// mov eax, DWORD PTR _N$[esp-4]
// mov ecx, DWORD PTR ?b@@3PAHA <--- this one
// mov eax, DWORD PTR [ecx+eax*4]
}



Again, there's an additional dereferencing in the pointer version, but not in the array version.

One situation where the additional dereference does not occur is when working on the stack and there are enough registers to store the pointer address. Since the pointer is already in a register, there is no dereference necessary. A typical example is:


int ptr(int N) {
int *b = new int[5];
// Note: 'new' loads the pointer address into 'eax' automatically
return b[N];
// mov ecx, DWORD PTR _N$[esp]
// mov eax, DWORD PTR [eax+ecx*4]
// add esp, 4
}




int arr(int N) {
int ar[5];
int a = std::rand();
int b = std::rand() % a;
int c = std::rand() % b;
int d = std::rand() % c;
N = (N + a + b + c + d) % 5;
// Note: This loads N into edx
return ar[N];
// mov eax, DWORD PTR _ar$[esp+edx*4+20]
}

int ptr(int N) {
int *p = new int[5];
int a = std::rand();
// mov DWORD PTR _p$[esp+20], eax
int b = std::rand() % a;
int c = std::rand() % b;
int d = std::rand() % c;
N = (N + a + b + c + d) % 5;
// Note: This loads N into edx
return p[N];
// mov eax, DWORD PTR _p$[esp+16] <--- here
// mov eax, DWORD PTR [eax+edx*4]
}


As shown above, the sequence of register-occupying numbers forces the compiler to place the pointer value from eax back on the stack, which forces a load back into memory. By contrast, since the address of an array is implicitly encoded in the esp pointer (by means of a constant offset), filling the registers has no impact at all.

So, I stand by my assertion that using a pointer requires an additional dereference when compared to an array, which may be optimized out if the pointer is an auto variable and can be kept on the stack.

An array reference, however, does indeed create the same dereference as a pointer (because, after all, it is a reference).


int arr(int N, int (&ar)[5]) {
return ar[N];
// mov eax, DWORD PTR _ar$[esp-4]
// mov ecx, DWORD PTR _N$[esp-4]
// mov eax, DWORD PTR [eax+ecx*4]
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this