Passing an Array to a Function?

Started by
9 comments, last by paulecoyote 19 years, 4 months ago
Hey all, yea I need some help on passing an array to a function. Heres the error I'm getting, followed by the code I have. h:\Programming\Lab 6 Strings n Arrays\main.cpp(54): error C2664: 'GetStudent' : cannot convert parameter 1 from 'char [10][30]' to 'char *'

#include <iostream>		// For console input output
#include <fstream>		// File input and output

using namespace std;	// Lets the compiler to use the namespace std 

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
///////////////////////// Structures /////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////// Function Prototypes /////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

int Menu();										// Displays the Menu
void FlushCin(void);							// Flushes the buffer 
void GetStudent(char* szName, double* dMark);	// Get Student info 

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////// MAIN FUNCTION ///////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

int main() {

    // This is where we will declare the variables that need to be used
	// within main!
	char szName[10][30];
	double dMark[10] = {0};
	

	// Call The Menu Display
	switch(Menu()){
		case 1:
			GetStudent(szName, dMark);
			break;
		case 2:
			cout << "You have entered case 2\n\t";
			break;
		case 3:
			cout << "You have entered case 3\n\t";
			break;
		case 4:
			cout << "You have entered case 4\n\t";
			break;
		case 5:
			cout << "You have entered case 5\n\t";
			break;
		case 6:
			cout << "You have entered case 6\n\t";
			break;

		default:
			cout << "There has been a fatal error, now terminating\n\t";
			return 0;
	}

	// Pause program
	system("pause");

	// Return to windows
	return 0;
}


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/////////////////////// Secondary Functions //////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////


/*******************************************************
	Function:		int Menu()
	Description:	Displays a menu for the user, returns
					the selection.
	Parameters:		None
	Returns:		int - User selection.
********************************************************/

int Menu() {
	
	// Local Storage
	int iTemp = 0;

	// Loops till correct input is received
	while( iTemp > 6 || iTemp < 1 || cin.fail() ) {

		system("cls"); // Clears the screen everytime it loops
		FlushCin();	// Flushes Cin on every loop

		// Displays Menu
		cout << "\n\t\t\tStudent Information Services\n";
		cout << "\n\t\t1) Input Students";
		cout << "\n\t\t2) Display Students";
		cout << "\n\t\t3) Show Highest Grade";
		cout << "\n\t\t4) Save Students to File";
		cout << "\n\t\t5) Load Students from File";
		cout << "\n\t\t6) Exit";
		cout << endl;
		cout << "\n\t\t";

		// Stores users input into a temporary variable
		cin >> iTemp;
	}

	return iTemp;
}

/*******************************************************
	Function:		FlushCin
	 Description:   This function clears extra characters out 
	  of the buffer
	Parameters:		void
	Returns:		void
********************************************************/
void FlushCin(void) 
{ 
   int iNum; // number of char in buffer 

   cin.clear(); 
   iNum = cin.rdbuf()->in_avail(); 
   cin.ignore(iNum, '\n'); 
   return;  
} 

/*******************************************************
	Function:		void GetStudent()
	Description:	This function will input the students
					name and grade.  Also keeps track of
					how many names are in the array.
	Parameters:		None
	Returns:		void - For now
********************************************************/
void GetStudent(char* szName, double* dMark){

	for (int i=0; i<10; i++)
		cout << "The value of dMarks at index " << i << "is " << dMark << endl;

	return;
}



I'm lost, I can't seem to get the array to pass, for the string anyways. I would greatly appreciate the help. Okay, It seems to be working so far with these changes

void GetStudent(char szName[10][30], double* dMark)
void GetStudent(char szName[][30], double* dMark)

// Both the top work, does anyone know which would be better
// Better coding practice or anything? Would one maybe cause
// unwelcome errors later?

Though, I'm not sure if that is the correct way, if it isn't please let me know :) Thanx for the reply, I tried the double pointer thing, couldn't seem to get it to work, but now the one I figured out and the one you gave me works, Thanx man. [Edited by - Surg AKA Kunark on December 2, 2004 12:20:44 AM]
~~Johnathan~~LTM my new fad, know it, use it, love it, LAUGHING TO MYSELF
Advertisement
you're trying to pass a two dimensional array, but you're only passing a single pointer. it looks like you actually need to pass a double pointer. try this void GetStudent(char** szName, double* dMark);

or you can do it like this if the first one doesn't work.
void GetStudent(char szName[][30], double *dMark);
Quote:Original post by Surg AKA Kunark
Though, I'm not sure if that is the correct way, if it isn't please let me know :)

Thanx for the reply, I tried the double pointer thing, couldn't seem to get it to work, but now the one I figured out and the one you gave me works, Thanx man.

Surg, both of your GetStudent functions are actually the same (the one where you have 10 and the one where you leave the brackets empty). The 10 in that parameter type is just syntactic fluff. If you are only going to be passing arrays of size [10][30], then prefer doing:

void GetStudent(char (&szName)[10][30], double* dMark)
Arrays are usually implicitly passed by reference to functions, never by value. Otherwise you end up copying an array onto the calling stack, which would suck.

Anyway, so when you have in parameter

char [30] - it only expects arrays of [30]
char [] - allows any size array


Note how you can do

char string[5]="blah";
char* string_pointer = string;

... without casting. string is actually a pointer to the first element in a contigous set of memory.

if you did

cout << (string+1)

you would get "lah"

the [] bit just reserves that much memory. +1 increments the pointer the size of one char along in the array, then cout outputs the rest to the null terminator.

when you have a [][] pointer, it's a different layout of memory then just a []. Same how [] is different kind of pointer then just a *... if you were to do this dynamically

char[] string = new char[] = "blah";

you would have to use

delete [] string

rather then

delete string

... or you would get memory leaks because it needs to delete an array type pointer, not a normal pointer.


As far as gotchas go... you could take a leaf from the Win32API and have int parameters indicating the size of the arrays, or do as Polymorphic OOP suggests. Though be warey that his method of

char (&szName)[10][30]

is roughly equivalent too

char &*

which means you are passing a reference to a pointer - which means you would actually be able to set the value of szName from the calling function to something different, rather then just the data its pointing too.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Quote:Original post by paulecoyote
Anyway, so when you have in parameter

char [30] - it only expects arrays of [30]
char [] - allows any size array

Actually, both of those are equivalent, which are also equivalent to doing char*. They imply no size restrictions since you are passing a pointer to the element type. You can even just pass the address of a char to any of them.

If you need to restrict the size you have to pass by pointer or reference to the array, as I described in my previous reply.

Quote:Original post by paulecoyote
char string[5]="blah";
char* string_pointer = string;

... without casting. string is actually a pointer to the first element in a contigous set of memory.

Actually, that is an implicit operation -- string isn't actually a pointer, however it can yield a pointer to the first element if one is required for the expression to be valid. This is similar in concept to using function pointers -- a function is not a pointer, however using the function name in a place where a function pointer is required will implicitly take the address of the function (only with arrays it takes the address of the first element, not of the array itself, which is different).

Quote:Original post by paulecoyote
char[] string = new char[] = "blah";


That's not a valid statement.

Quote:Original post by paulecoyote
As far as gotchas go... you could take a leaf from the Win32API and have int parameters indicating the size of the arrays, or do as Polymorphic OOP suggests. Though be warey that his method of

char (&szName)[10][30]

is roughly equivalent too

char &*

which means you are passing a reference to a pointer - which means you would actually be able to set the value of szName from the calling function to something different, rather then just the data its pointing too.

Nope, actually it's not roughly equivalent in any way to a reference to a pointer (although you wrote a pointer to a reference which isn't valid in C++). An array reference is a reference just like any other, there is no pointer envolved whatsoever. The array reference I provided is just like a reference to an int or a float or any other type -- there are no extra gotchas. You can't set szName to refer to something else just like you can't make any other reference reference something else after initialization.
Yeah, before first coffee, so some of what you said is true about the new thing. Few typos, getting distracted then doing this, blah blah blah.

However *& is valid in C++. Though reading through your post I think you did say that, you just picked up on the &* typo.

Just in case I misunderstood, I believe the "emule" source has examples of *&. You can follow the links and download if u like:
http://www.emule-project.net/home/perl/general.cgi?l=1


anyway... the (&char)[x][y] thing for a parameter definition threw me, because

& = address operator

... but arrays are implicitly sent by reference anyway... so u r sending a reference to a reference. Which isn't the case because otherwise the below snippet would be disproved. Interestingly enough...

char [x][y] allowd the parameter to be a l-value, but (&char) makes it into an r-value. Now like this Fred Swartz guy (below) I thought it did nothing... but doing & seems to make the argument in a parameter list into an r-value.

(For OP - l-value == left hand side of assignment in variable expression, r-value = right hand side.

l-values values can be set. r-values values cannot. l-values can be used for r-values (on the right hand side), but an r-value only cannot be used on the left.

EG
a and b are l-values
1 is a r-value

1 = a; // Doesn't work
a = 1; // Does work
a = b = 1; //Does work

)


...


(Web Quote - though Deitel & Deitel supports)
http://www.fredosaurus.com/notes-cpp/arrayptr/arraysaspointers2.html

& operator applied to arrays does nothing.

The & (address of) operator normally returns the address of the operand. However, arrays are the exception. When applied to an array (which is an address), it has the same value as the array reference without the operator. This isn't true of the equivalent pointers, which have an independent address. The example below show this. When cout is given an address, it prints it in hexadecimal (except addresses of characters, which are assumed to be the beginning of a c-string). For example,

// arrayptr.cpp - Show operation of & on arrays and pointers.//   Fred Swartz - 2002-09-25#include <iostream>using namespace std;int main() {    double  a[100];  // array    double* p = a;  // pointer equivalent to a    cout << "a=" << a << ", &a=" << &a << endl;    cout << "p=" << p << ", &p=" << &p << endl;    return 0;}


This produces the following output showing the unexpected equality a == &a. The value of p is the same as a as expected, and &p is the address of the memory location of the p variable as expected.

a=006AFAD8, &a=006AFAD8
p=006AFAD8, &p=006AFAD4


Note the actual numbers will likely be different.

So I would actually like to see where the behaviour of using a & operator with an array is actually defined as having this effect of making it a r-value only when used in a parameter list. Any web links would be useful.

EDIT:
So to sum up:

array == pointer of same type
& array == array

... by the actual memory locations, and to some extent usage... the above is correct. They are the same. But

pointer != & pointer
& array != & pointer


... by the by you are a harsh man if you are the one whom just rated me down.


EDIT 2:
So... & & is illegal, &* is illegal, but *& is not. All arrays are implicitly pointers to the first element (as above snippet proves - a == p). By putting & in (char&)[][] it's like a &* which is illegal. But the compiler lets u get away with it and just thinks well... you actually did just mean the array reference... not an array reference reference.

The other way of interpreting it is that it's a *&, but that's ignored because compiler doesn't think you really mean to send across a reference to an array - as all arrays are passed that way anyway. Hence why a == &a. Still doesn't explain the r-value thing though when used as parameter.

[Edited by - paulecoyote on December 2, 2004 7:20:25 AM]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Quote:Original post by paulecoyote
Yeah, before first coffee, so some of what you said is true about the new thing. Few typos, getting distracted then doing this, blah blah blah.

However *& is valid in C++. Though reading through your post I think you did say that, you just picked up on the &* typo.

Yes, I know references to pointers are valid, I never said they weren't. I was just pointing out that the example you gave was actually a pointer to a reference, which is not valid.

Quote:Original post by paulecoyote
anyway... the (&char)[x][y] thing for a parameter definition threw me, because

& = address operator

... but arrays are implicitly sent by reference anyway... so u r sending a reference to a reference. Which isn't the case because otherwise the below snippet would be disproved.

Not exactly. The standard allows parameters using [] or [some_value] as syntactic fluff, which actually mean the same thing as a pointer to the element type, which is not a pointer to the array. Using that syntax does not pass the array by reference, but rather, it passes the first element by pointer, which only gives you some similar syntactic use (and gets rid of the array type information).

My example does not in any way mean a reference to a reference or a reference to a pointer. You are confused due to the misconception that an array is a pointer, which is an all too common mistake. Array types are completely separate from pointer types, and different size arrays are different types as well. They are not pointer nor reference types, but rather they are types which represent exactly what an array is -- a contiguous block of a certain number of items of a given type.

your_type (&your_array_reference)[your_array_size] = some_array


In that example, "your_array_reference" is a reference to "some_array" whose elements are of type "your_type" and whose size is "your_array_size." It's not a reference to a pointer, it's a reference to the array and can be used in every way just as though you were working with "some_array." Again, there are no extra gotchas. It's a reference just like a reference to any other type, only here it's to an array.

That is all that my example does -- it makes the function take a reference to the array instead of a pointer to the first element. This keeps the array being referenced as it should -- an actual array of a specific size, not just utilizing a pointer to the first element to provide some similar syntax, and it also disallows passing arrays of different sizes, etc. This leaves less room for error since passing an array of a different size will give an error at compile time (as opposed to possibly getting an access violation at runtime), and since it is a reference and not a pointer, it disallows the passing of an uninitialized pointer or a null pointer, unlike if you were using a pointer.

Quote:Original post by paulecoyote
(Web Quote - though Deitel & Deitel supports)
http://www.fredosaurus.com/notes-cpp/arrayptr/arraysaspointers2.html

& operator applied to arrays does nothing.

The & (address of) operator normally returns the address of the operand. However, arrays are the exception. When applied to an array (which is an address), it has the same value as the array reference without the operator. This isn't true of the equivalent pointers, which have an independent address. The example below show this. When cout is given an address, it prints it in hexadecimal (except addresses of characters, which are assumed to be the beginning of a c-string). For example,

*** Source Snippet Removed ***


Unfortunately, this is why people never learn properly -- the sources they learn from are incorrect. I can't fully express how angry that link made me, since it provides incorrect information to people learning the language! An array is in no way a pointer, as I have stated. There is absolutely no exception to using the address of operator on an array -- it gives you a pointer to the array type. For instance:

int array[10];int (*pointer)[10] = &array


&array gives you the address of the array, which is of the type int (*)[10].

This is because an array is not a pointer. It is its own entity that is capable of implicitly yielding a pointer to its first element. This is done so that you don't have to type &array[0] every time you need a pointer to the first element (such as for passing a pointer to the first element to a function). Again, this in no way makes array a pointer, it's just a way to implicitly perform an operation that you'd otherwise have to explicitly write out. When you make an array, no pointer is created -- all that happens is a chunk of contiguous memory is created for the elements. Using the array name where a pointer to the element type is expected will just automatically take the address of the first element.


Quote:Original post by paulecoyote
This produces the following output showing the unexpected equality a == &a. The value of p is the same as a as expected, and &p is the address of the memory location of the p variable as expected.

This is true for the same reason that the address of the first element of a structure is the same value as the address of the structure itself. This in no way means that the first element of the structure and the element as a whole are the same thing. They are two completely different entities who just share the same starting location in memory. While the value of the address of the first element of "a" and the value of the address of "a" itself are the same, they are completely different types and mean completely different things! For instance:

int array[10];int* this_will_work = array; // Implicitly yields the address of the first element, okayint* this_will_not_work = &array // ERROR because &array's type is actually int (*)[10]


This correctly will give you error because &array is not the same as &array[0]. The former is a pointer to the array type while the latter is a pointer to the element type.

You can also see this by outputting typeid( &array ).name() and comparing it to typeid( array ).name() as well as typeid( &array[0] ).name(). All three should give you different types.



Edit in response to your edit:

Quote:Original post by paulecoyote
EDIT 2:
So... & & is illegal, &* is illegal, but *& is not. All arrays are implicitly pointers to the first element (as above snippet proves - a == p). By putting & in (char&)[][] it's like a &* which is illegal. But the compiler lets u get away with it and just thinks well... you actually did just mean the array reference... not an array reference reference.

char (&your_reference)[some_size][some_other_size]


in no way means &*. You are again just confused because you think that an array is a pointer which is completely untrue. The compiler isn't "letting you get away with it." It works because it literally means a reference to the array -- not a pointer to a reference or anything of the sort. There is nothing invalid about it! An array is an array is an array is an array. It is not, however, a pointer.

Quote:Original post by paulecoyote
The other way of interpreting it is that it's a *&, but that's ignored because compiler doesn't think you really mean to send across a reference to an array - as all arrays are passed that way anyway. Hence why a == &a. Still doesn't explain the r-value thing though when used as parameter.

It means neither of the two things you are describing. Again, you are just not understanding that arrays are their own types and are in no way pointer types. Passing an array like that means you are passing the entire array by reference. The way you were doing it, on the otherhand, actually passes the first element of the array by pointer, which is completely different. You are just confused by the syntactic similarities in usage between arrays and pointers.

[Edited by - Polymorphic OOP on December 2, 2004 7:23:47 AM]
yeah I see, I know they are different types (arrays versus pointers) - very similar but not the same. Hence why

delete []

is required for arrays rather then

delete

... for normal dynamic vars.

In a one dimensional array:
int i[10];int *pI1 = i;    //Pointer to First element in arrayint *pI2 = i+1;  //Pointer to Second element in arrayint *pI3 = pI1+1;  //Pointer to Second element in array/* NOTE: Coding like this probably kills kittens, this is only for illustration */



... would work because it's 1d. In a >1d array it won't. That's probably where people fall into the trap thinking they are interchangable - because it's made to seem that way. As that snippet I included before shows.


Still... that bit:

a == &a

... is pretty wierd behaviour. But I guess you could do that with your own types if you overrode that operator.

(char&)[][] being a r-value where as char[][] is a l-value in a parameter list is pretty different from usual usage of & too. It's cool though cos you kind of make the array variable a const (by making it a r-value) without making the contents a const (which would happen with keyword const being used).

Well it's all useful stuff to know anyway, feel like I learned something - though right now I'm not sure what. Something academic anyway. The whole & effect on parameter marshalling arrays, yeah that's it.

EDIT: Evil Source code format
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Quote:Original post by paulecoyote
a == &a

... is pretty wierd behaviour. But I guess you could do that with your own types if you overrode that operator.

You don't have to overload operator& to understance it better. Here is an example of it only with aggregate structs the way I described earlier:

struct foo{  int bar;} foo_instance;


The value of the address of foo_instance.bar is the same as the value of the address of foo_instance. The value of &array[0] and the value &array are also equal (remember, only in address value, not type nor logic). This is why outputting array or &array[0] and &array will give you the same value, since the value of the address is the same. This in no way means that they are the same, just like it in no way means that &foo_instance.bar and &foo_instance are the same. It just so happens that their starting address is the same -- they are still logically different concepts.

Quote:Original post by paulecoyote
would work because it's 1d. In a >1d array it won't. That's probably where people fall into the trap thinking they are interchangable - because it's made to seem that way. As that snippet I included before shows.

It can still work with higher dimensions, only you have to remember that a multidimensional array is actually an array of arrays. That means that the element type of array isn't int, but rather, an array of ints of the appropriate size. So to iterate, you have to use a pointer to the element array type.

int array[10][5]int (*iterator)[5] = array;


Quote:Original post by paulecoyote
(char&)[][] being a r-value where as char[][] is a l-value in a parameter list is pretty different from usual usage of & too. It's cool though cos you kind of make the array variable a const (by making it a r-value) without making the contents a const (which would happen with keyword const being used).

The reason why char (&)[some_value] is an r-value is because it directly references the array, which can only in practice be used as an r-value. The reason why char[] in a parameter list is an l-value is because, again, it does not mean you are passing an array. It is syntactic fluff for passing a char*. Since char* is a non-const pointer, it is an l-value. What I find more strange than the concept of array pointers and references is the fact that char[] and char[some_size] in a parameter list don't mean what what people would expect them to, which only contributes to people's misunderstanding of arrays.

Quote:Original post by paulecoyote
Well it's all useful stuff to know anyway, feel like I learned something - though right now I'm not sure what. Something academic anyway. The whole & effect on parameter marshalling arrays, yeah that's it.

Yay.
(Another post incase you are already replying in length to the last:

Lesson 2: Arrays aren't like normal types when passed by reference - arrays are actually always addresses to arrays, which is why &a doesn't yield anything different to a.

Lesson 3: Arrays impose slight differences on language usage versus use on built-in and even complex types (hence delete[] instead of delete)
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.

This topic is closed to new replies.

Advertisement