Sharing a name between functions

I'm building a game and I have a user name validation that works. The code basically looks 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
void Introduction();
void Adventure();

int main(){
Introduction();
Adventure();
return 0;}

void Introduction(){
    cout<<"Blah blah, adventure stuff. Input your name.";
    
    char name[10];
    int lengthcheck;
    do{cin.clear();
    cin.sync();
    cin.getline(name,10);
    lengthcheck=strlen(name);
    if(lengthcheck>8){cout<<"That's too long. What do people call you?\n";}
    }while(lengthcheck>8);
    
    cout<<"Good, your name is "<<name<<".";}

void Adventure(){
    char name[10];
    cout<<"Just making sure your name is still "<<name<<".";}


That works. It remembers your name in the Adventure function. Now, I need to use that input validation a lot, so I decided to make it into a function. That's when things break.

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
void Introduction();
void Namecheck();
void Adventure();

int main(){
Introduction();
Adventure();
return 0;}

void Introduction(){
    cout<<"Blah blah, adventure stuff. Input your name.";
    char name[10];
    Namecheck();
    cout<<"Good, your name is "<<name<<".";}

void Namecheck(){
    char name[10];
    int lengthcheck;
    do{cin.clear();
    cin.sync();
    cin.getline(name,10);
    lengthcheck=strlen(name);
    if(lengthcheck>8){cout<<"That's too long. What do people call you?\n";}
    }while(lengthcheck>8);}

void Adventure(){
    char name[10];
    cout<<"Just making sure your name is still "<<name<<".";}


So main calls Introduction, and Introduction then calls Namecheck. Namecheck does perform its job, but when Introduction says "Good, your name is" it gives a bunch of symbols. The same is true when Adventure double checks your name.

So how can I fix this?
Either place the name variable globally or pass it to the name check function when you call it. A variable declared locally is not accessible by everyone who declares the same variable with the same name.
Well, if I declare the name in the main, but don't declare it in the function, then it won't compile. Like so:
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
void Introduction();
void Namecheck();
void Adventure();

int main(){
char name[10];
Introduction();
Adventure();
return 0;}

void Introduction(){
    cout<<"Blah blah, adventure stuff. Input your name.";
    //char name[10];
    Namecheck();
    cout<<"Good, your name is "<<name<<".";}

void Namecheck(){
    //char name[10];
    int lengthcheck;
    do{cin.clear();
    cin.sync();
    cin.getline(name,10);
    lengthcheck=strlen(name);
    if(lengthcheck>8){cout<<"That's too long. What do people call you?\n";}
    }while(lengthcheck>8);}

void Adventure(){
    //char name[10];
    cout<<"Just making sure your name is still "<<name<<".";}
If I do declare the name in the functions and also declare it in the main, then I run into the problem in my original post. Can you explain exactly what you mean by passing it to the name check function? I don't want to make any guesses.
Last edited on
That works. It remembers your name in the Adventure function.


No it doesn't. Or at least if it does, it's a fluke that it does. You are relying on undefined behavior here which is very very bad.

This is a scope issue. A variable exists only inside its current scope -- scope is determined usually by the bounding {curly braces}. Example:

1
2
3
4
5
6
7
8
9
int main()
{
  { // some braces to create a scope
    int foo = 5;
    cout << foo;  // prints '5' as you'd expect
  } // <- foo's scope ends here

  cout << foo;  // COMPILER ERROR - 'foo' doesn't exist any more
}


As this example shows, 'foo' does not exist outside its scope. As soon as its scope ends, the variable is destroyed and its contents lost.

You have the same issue in your first code snippit. Your 'name' variable on line 12 has a scope that limits it to the 'Introduction' function. That variable does not exist outside of that function.

The 'name' you create on line 24 is not the same variable. It doesn't matter that it is also called 'name'... it's an entirely different block of memory that has its own contents.



The key problem here is that 'name' is local to 'Introduction'... which means as soon as 'Introduction' exits, you lose the name. You need to give 'name' a broader scope.

One way to do this is to create 'name' local to 'main', and pass to to whatever sub functions that need it as a parameter:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
    char name[10];
    Introduction( name );
}

void Introduction( char* username )
{
    // do stuff with 'username' here
    //   since main passed in 'name' as this parameter, reading or modifying
    //   Introduction's 'username' is the same as reading/modifying main's 'name'
}


In this case, 'name' has a scope local to main, which means it will stick around as long as main() is still running (which will be pretty much the entire lifetime of the program). Since we want to use 'name' outside of main, we can pass it as a parameter to whatever other functions that need it.
http://www.cplusplus.com/doc/tutorial/functions/ (scope)
`name' in main(), `name' in Introduction(),`name' in Namecheck(), `name' in Adventure() are all different variables.
The identifier is the same, but that's irrelevant.

You need to pass it as a parameter http://www.cplusplus.com/doc/tutorial/functions2/


> That works. It remembers your name in the Adventure function.
Nope, that's undefined behaviour. You are using (printing) a variable that was not initialized, it contains garbage.
Also... if you want a string, you should really use a string and not a char array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <string>

void Introduction( std::string& username );  // <- note the & here because we are passing by reference

int main()
{
    std::string name;

    Introduction(name);
}

void Introduction( std::string& username )
{
    // blah blah, modifying 'username' here will modify main's 'name'
}
The third approach that nobody mentioned is to write a class that will have data member name. In this case all member functions may have no parameters.:)
Man, you guys are great. It's 99% of the way there.
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
#include <iostream>
#include <string>
using namespace std;

void Introduction(string& name);
void Namecheck(string& name);
void Adventure(string& name);

int main(){
string username;
Introduction(username);
Adventure(username);
system("pause");
return 0;}

void Introduction(string& name){
    cout<<"Blah blah, adventure stuff. Input your name.\n";
    Namecheck(name);
    cout<<"Good, your name is "<<name<<".";}

void Namecheck(string& name){
    int lengthcheck;
    do{cin>>name;
    if(name.length()>8){cout<<"That's too long. What do people call you?\n";}
    }while(name.length()>8);}

void Adventure(std::string& name){
    cout<<"\nJust making sure your name is still "<<name<<".\n";}

It gives me:
Blah blah, adventure stuff. Input your name.
BillClinton
That's too long. What do people call you?
Bill
Good, your name is Bill.
Just making sure your name is still Bill.
Press any key to continue . . .

There's just one problem:
Blah blah, adventure stuff. Input your name.
Bill Clinton
Good, your name is Bill.
Just making sure your name is still Bill.
Press any key to continue . . .

I thought strings included spaces, so what's it doing now?
strings do include spaces.

However the >> extraction operator for cin stops at the first whitespace. You want to use getline() to get the entire line.

1
2
3
cin >> name;  // get rid of this

getline( cin, name );  // replace it with this 
Ah, wonderful. You guys are on the ball. Everything works perfectly now.

This has got me thinking: eventually I want to include a rudimentary check on, for example, whether or not the player has a sword. It could be done with the same process. A global 'int sword' in main that is 0 if the player doesn't have one, or 1 if they do. Then you'd add a reference argument to function so that it can see 'sword' in main, just like we did with 'name' and 'username'.

However, I could imagine these checks could get unmanageable pretty quickly. Checks for swords, lamps, whatever, all clogging up the list of arguments. If we turned the whole thing into a class, I should be able to declare all the variables, and then use them in the functions without adding any arguments, right?

Something like...
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
class Game
{private:
int sword, lamp;
string name;

public:
Game()
{introduction();
adventure();};

void introduction()
    {cout<<"Blah blah, adventure stuff. Input your name.\n";
    namecheck();
    cout<<"Good, your name is "<<name<<". Take this sword.";
    sword=1;}

void namecheck()
    {do{getline(cin,name);
    if(name.length()>8){cout<<"That's too long. What do people call you?\n";}
    }while(name.length()>8);}

void adventure()
     {cout<<"\nJust making sure your name is still "<<name<<".\n"
     "Do you have a sword?";
     if(sword==1)cout<<"Then you'll be fine.";
     else
     cout<<"Then you'd better get one!";}
}
Last edited on
Topic archived. No new replies allowed.