Passing an array to a function w/ a pointer.

This topic is 4182 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Hello all, I'm trying to learn ANSI standard C Plus Plus, and I'm working through the tutorials on cplusplus.com, and I've come to a bit of a sticky point. Here is the code I have:
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

struct employee{
int emp_id;
string employee_name;
float salary;
string position;
};

void printemployee(employee emp[], int i);

int main()
{
int i,n;
int *p;

cout << "How many employees do you wish to enter? ";
cin >> i;

p = new (nothrow) employee;
if (p==0)
cout << "Error: Memory couldn't be allocated.";
else
{
for (n=0; n<i; n++)
{
string mystr;

cout << "Please enter the employee's name: ";
getline(cin, p[n].employee_name);

cout << "Please enter the employee's ID Number: ";
getline(cin, mystr);
stringstream(mystr) >> p[n].emp_id;

cout << "Please enter the employee's salary: ";
getline(cin, mystr);
stringstream(mystr) >> p[n].salary;

cout << "Please enter the employee's position: ";
getline(cin, p[n].position);
}
printemployee(p, i);

}

return 0;
}

void printemployee(employee emp[], int i)
{
int n;
for (n=0; n<i; n++)
{
cout << "Name:     " << emp[n].employee_name << endl;
cout << "ID:       " << emp[n].emp_id << endl;
cout << "Salary:   " << emp[n].salary << endl;
cout << "Position: " << emp[n].position << endl << endl;
}

}


What I was trying to do is to combine what I've learned about structures, dynamic memory and functions to allow for a user generated array of the type "employee", and have the printemployee() function print out all the people entered thus far. Right now I'm getting eight errors, and logically speaking, I'm not sure what I'm doing wrong... Oh... And I forgot the mandatory question for the beginners forum: "Doodz-like I am totalllly l337 and I want to make a WoW killin' MMORPG. I dont no how too program, but that shoudnt be aproblem, right?" :P Thanks! Evan.

Share on other sites
Quote:
 Original post by E_LaneRight now I'm getting eight errors, and logically speaking, I'm not sure what I'm doing wrong...

Are these compiler errors, linker errors or runtime errors? Please post the error messages you are getting.

Share on other sites
This is what was in the build output box:
They're compiling errors, and I'm using Visual C++ 2005 Express, if that matters.

1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(26) : error C2440: '=' : cannot convert from 'employee *' to 'int *'1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(36) : error C2228: left of '.employee_name' must have class/struct/union1>        type is 'int'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(36) : error C2780: 'std::basic_istream<_Elem,_Traits> &std::getline(std::basic_istream<_Elem,_Traits> &,std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem)' : expects 3 arguments - 2 provided1>        c:\program files\microsoft visual studio 8\vc\include\string(476) : see declaration of 'std::getline'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(40) : error C2228: left of '.emp_id' must have class/struct/union1>        type is 'int'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(44) : error C2228: left of '.salary' must have class/struct/union1>        type is 'int'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(47) : error C2228: left of '.position' must have class/struct/union1>        type is 'int'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(47) : error C2780: 'std::basic_istream<_Elem,_Traits> &std::getline(std::basic_istream<_Elem,_Traits> &,std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem)' : expects 3 arguments - 2 provided1>        c:\program files\microsoft visual studio 8\vc\include\string(476) : see declaration of 'std::getline'1>c:\documents and settings\elane\my documents\visual studio 2005\projects\9-13-06-900\9-13-06-900\test.cpp(49) : error C2664: 'printemployee' : cannot convert parameter 1 from 'int' to 'employee []'1>        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast

Share on other sites
You need to declare p as an employee *, rather than an int *.

Share on other sites
p = new (nothrow) employee;

p is a pointer to an integer. You probably want
employee * p = new (nothrow) employee;

That should solve most of your errors.

Share on other sites
Gah! Of course! Thanks!
Now the only problem is that the codes seems to skip the first itiration of "Enter employee name: " and goes directly to Employee Number.
#include <iostream>#include <string>#include <sstream>using namespace std;struct employee{	int emp_id;	string employee_name;	float salary;	string position;};void printemployee(employee emp[], int i);int main(){	int i,n;	employee *p;	cout << "How many employees do you wish to enter? ";	cin >> i;	p = new (nothrow) employee;	if (p==0)		cout << "Error: Memory couldn't be allocated.";	else	{		for (n=0; n<i; n++)		{			string mystr;						cout << "Please enter the employee's name: ";			getline(cin, p[n].employee_name);						cout << "Please enter the employee's ID Number: ";			getline(cin, mystr);			stringstream(mystr) >> p[n].emp_id;						cout << "Please enter the employee's salary: ";			getline(cin, mystr);			stringstream(mystr) >> p[n].salary;			cout << "Please enter the employee's position: ";			getline(cin, p[n].position);		}		printemployee(p, i);	}	return 0;}void printemployee(employee emp[], int i){	int n;	for (n=0; n<i; n++)	{		cout << "Name:     " << emp[n].employee_name << endl;		cout << "ID:       " << emp[n].emp_id << endl;		cout << "Salary:   " << emp[n].salary << endl;		cout << "Position: " << emp[n].position << endl << endl;	}}

Subsequent times, entering employee name works. Just not the first.

Share on other sites
Oh one other question:
By making a structure with more than one variable, and creating an array of that structure, does that make the array multidimensional?

Share on other sites
Quote:
 Original post by E_LaneOh one other question:By making a structure with more than one variable, and creating an array of that structure, does that make the array multidimensional?

No. The members of a structure do not constitute an array dimension.

Quote:
 Now the only problem is that the codes seems to skip the first itiration of "Enter employee name: " and goes directly to Employee Number.

Are you, by any chance, using Microsoft Visual C++ 6.0? There was a bug in its STL implementation (provided by Dinkumware) regarding std::getline that generated that behavior. I recommend upgrading to Microsoft Visual C++ 2005 Express Edition, for multiple reasons.

Share on other sites
Quote:
 Original post by OluseyiAre you, by any chance, using Microsoft Visual C++ 6.0?

His output box had "Visual Studio 2005" in the path, so I assumed not, but that's good advice either way.

This behavior is also common if there's a newline character (\n) in the input stream by the time getline gets to it (as is common in loops). Since it reads up to a newline character, it will just take the first \n left in the stream.

You may want to look up the msdn article on cin.ignore().

Does the cout << "Please enter employee name"; get printed, at least?

Share on other sites
Quote:
 Original post by templewulfThis behavior is also common if there's a newline character (\n) in the input stream by the time getline gets to it.

Which there will be, left over from answering "How many employees do you wish to enter? ", since the answer to that question is read by directly using cin, rather than using getline.

Share on other sites
templewulf:
Yes it gets printed.
I looked at the cin.ignore documentation, and implimented it as such
getline(cin.ignore(2), p[n].employee_name);

which worked perfectly. Thanks for the help!

Share on other sites
You used to write in C?

At the least, you have a Cism in your code.

There is no need to declair variables at the start of a function. It is usually considered a better practice to declair variables as close as reasonable to where they are initialized and used.

	int i,n;	int *p;	cout << "How many employees do you wish to enter? ";	cin >> i;	p = new (nothrow) employee;	if (p==0)		cout << "Error: Memory couldn't be allocated.";

Change that to

	cout << "How many employees do you wish to enter? ";	int i;	cin >> i;	employee* p = new (nothrow) employee;	if (p==0)		cout << "Error: Memory couldn't be allocated.";

----

And change this:
		for (n=0; n<i; n++)

to:
		for (int n=0; n<i; n++)

----

The next big issue is that you are newing memory you do not delete. This is a bad habit to get into.

There are better practices than what I will tell you to do -- but to start with, include at the end of the function:
delete[] p;

Other than that, you are looking good!

Share on other sites
Thanks,
I wasn't sure if 'new' was anything like malloc... Guess it is.

And yes, I've done some C coding, but it's been YEARS... And I'm as close to a newbie as you can get without the PineFresh car hanger slung around my neck..

Share on other sites
Do some googling on "RAII" -- resource allocation is initialization.

It is a pattern that really helps with the entire allocate/delete.

Share on other sites
cplusplus.com isn't very good from what I've seen.

#include <iostream>#include <string>// The stringstream technique you were using is a good way of making the input// more robust; if you just do that when you read 'i' as well, then you will// have no problems, because the newline is "consumed".// However, because we are going to do that several times, we'll make a helper// function for it.#include <sstream>// We're also going to need one more include:#include <vector>// This is a nice toy that we're going to use to avoid having to play with// memory allocation ourselves.// By the way, in normal C++ code you shouldn't use 'nothrow' delete, nor check// for success. The normal form of new cannot return NULL; it will throw an// exception on error. You then have much more freedom over how you handle // memory allocation failures. Most programs don't, because realistically you're// screwed anyway :)// But with std::vector, all memory allocation and deallocation will be handled// for us; the only thing we might have to do is handle an exception, but it's// usual to ignore them (the behaviour of an exception that falls all the way// through is quite well defined).using namespace std;struct employee {  // Don't include "employee" or short forms thereof in the names of "employee"  // data members. It's redundant and does not add extra information.  int id;  string name;  float salary;  string position;};// In C++, we can "make structs printable": just as formatted stream output // handles each primitive type differently according to the type, we can// define how it behaves for our own types:ostream& operator<<(ostream& os, const employee& e) {  // Within the operator overload, we don't want to flush the buffer, because  // that confuses responsibilities - and isn't consistent: other types don't  // do this. So instead, we'll just output the newlines:  return os << "Name:     " << e.name << '\n'            << "ID:       " << e.id << '\n'            << "Salary:   " << e.salary << '\n'            << "Position: " << e.position << '\n';  // Notice (a) we're outputting to a *parameter* 'os', instead of always to  // cout; it's the calling code's responsibility to define where to output to,  // and we can output to any stream now.  // (b) there's nothing about ending a line - of source or output text - which  // requires you to "start over".  // (c) the reason this "chaining" works is because the operator returns the  // stream object from the left hand side (by reference), so that it can be  // reused for the next operator invocation. We take advantage of that  // convention by doing it all in one statement, and respect the convention by  // returning the "os".}// Because we have code now to "print an employee", we can easily print all// employees:void printemployees(const vector<employee>& employees){	for (int n = 0; n < employees.size(); n++)	{		cout << employees << endl;	}}// Notice:// (a) now we can, as promised, just "feed each employee to cout" as if it were// of a built-in type.// (b) we can declare and initialize the counter variable as part of the for// statement.// (c) we can index the vector just as if it were an array.// (d) we pass the vector "by const reference": the '&' indicates// pass-by-reference, which is *like* passing by pointer (to avoid copying// something and "sharing" an object between caller and callee), but without// requiring the caller to address-of, or the function to dereference. For all// intents and purposes, it makes the vector inside the function *be* the// vector in the calling function (main(), for us).// (e) the vector "knows its size", so we don't have to pass that information// along at all.// (f) there are plenty of other ways to do this, some of which don't actually// involve writing a loop, but instead invoking "algorithms" from the standard// library. The C++ standard library is a wonderful thing. :) Anyway, this// function is pretty useless for what it does, but it illustrates a lot ;)// Here's our function for reading a line of input and interpreting it as the// given type. I'll again use a reference parameter; this time it is not// 'const', because we want to change the value locally (the caller will// automatically see the change, as if it had been done by the caller). This// function is templated (like the std::vector class is templated), so we can// use it for any type that can be read in by the >> operator; at compile-time,// the compiler can determine which version of the function to use according to// the compile-time type of the parameter we give it.// We'll return false, and make no change, to indicate that there was an // error in input. We'll make the change and return true to indicate that input// was successful. This allows us to know what happened if the input value// happened to equal the old value of the variable.template <typename T>bool input(istream& is, T& result){	// First, grab a line from the input.	string line;	getline(is, line);	// Now, try to read it out, and see if we're successful.	return stringstream(line) >> result;	// The operator>> returns the stringstream by reference, of course,	// but since we are returning a bool, an implicit cast occurs. The	// result tells us whether reading was successful.	// That property is often exploited like this:	// while (cin >> variable) { /* do something */ } /* reached EOF now */	// Since we read into a temporary stringstream, the input stream is	// will not have been "failed" by an invalid read, so we are OK.}// Of course, that doesn't work right for inputting a string, because it will// just grab the first word. So for that case we'll just use raw getline() :)// To be rigorous about the design, though, it would be a good idea to// "specialize" the template for that case... Oh, heck, I'll show that:template <>bool input(istream& is, string& result){	return getline(is, result); // nice and simple.	// Although now we lost the ability to chain getline calls because	// of the cast to boolean ;\}// By the way, notice how I've been putting the functions *ahead of* main().// This lets us avoid extra typing (and maintenance) for prototypes, and exposes// any circular dependencies (because we're forced to prototype for those).int main(){	// As was mentioned, in C++ there is no need to declare variables at	// the top of a scope, and it's generally agreed that you *should not* -	// instead, put them near first use.	cout << "How many employees do you wish to enter? ";	int i;	input(cin, i);	// Now we have the information we need to create our vector:	vector<employee> employees(i);	// This declares and initializes a single vector, via one of the	// "constructors" of std::vector: this one just takes an int value,	// and uses it as the size of the dynamic memory that is allocated.	// (A vector can be resized during its lifetime, but it will not	// automatically resize just because you ask for an array index beyond	// its current bounds. Also, when the vector resizes, the contents will	// relocate in memory, with the old memory allocation being cleaned up	// automatically. There *is* a member function available, .push_back(),	// which you can use to append to the end (automatically resizing if	// needed).	for (int n = 0; n < employees.size(); n++)	// Again, as noted, we don't need 'i' any more.	{		cout << "Please enter the employee's name: ";		input(cin, employees[n].name);					cout << "Please enter the employee's ID Number: ";		input(cin, employees[n].id);					cout << "Please enter the employee's salary: ";		input(cin, employees[n].salary);		cout << "Please enter the employee's position: ";		input(cin, employees[n].position);	}	printemployees(employees);	// You don't need to 'return 0' at the end explicitly. As a special	// case, reaching the end of main() returns 0.}