Scope in structs c++ - c++

I'm using C++ to make a program that fills a Deck struct with cards. Here is the Deck definition:
struct Deck{
char suit;
int value;
};
My problem is that the function using my struct, fillDeck, is not able to access the contents of the struct. Maybe I'm doing it completely wrong, but here is what I have:
void fillDeck(Deck *deck){
for (int num = 2; num <= 14; num++){
for(int count = 0; count < 4; count++){
if(count == 0){
suit = "clubs";
}
if(count == 1){
suit = "hearts";
}
if(count == 2){
suit = "diamonds";
}
if(count == 3){
suit = "spades";
}
}
}
}
My main function is as follows.
int main (int argc, char *argv[]){
Deck deck;
fillDeck(&deck);
}

Your main function will work if you change your struct member suit to a string.
#include <string>
struct Deck
{
std::string suit;
int value;
};
And then access it in fillDeck like so:
...
deck->suit = "clubs";
...

Declare your struct like this
struct Deck{
char* suit;
int value;
};
Then you can access it like this:
deck->suit

Using std::string is a good option if you need to keep the full name of the suits.
If you have to use character for suits then you may use abbreviation as follows:
void fillDeck(Deck *deck){
for (int num = 2; num <= 14; num++){
for(int count = 0; count < 4; count++){
if(count == 0){
deck->suit = `C`;
}
if(count == 1){
deck->suit = `H`;
}
if(count == 2){
deck->suit = `D`;
}
if(count == 3){
deck->suit = `S`;
}
}
}
}
The you can adjust the rest of your accordingly.
Also instead of the consecutive if statements you may want to try switch case statement, which may look better in that case.

In looking at what you're passing to the fillDeck(Deck *deck) function inside of your main(), I would say that you are probably passing your deck argument wrong as right now you are passing the address to a Deck data structure that you have yet to initialize...
I think the strategy you should take is to define a pointer of type Deck inside of your main function, maybe something like Deck *ptrDeck, and that pointer should then be pointed to an new struct of type Data. I do not really understand your problem set too well right now, but I would also think that maybe that pointer should really be pointing to an Array of type deck that will then hold each suit with its corresponding value. You will most likely have to populate said Array of type Data using a for loop to hold each suit and value. this can/should be done in its own function.
you can then create another function that would print each initialized instance of your Deck struct.
your for loop will probably have something like ptrDeck[i].suit = "whatever it is"...but then again you have your suits declared as type char so it seems to me that maybe you have to think this through a little...
im no expert, It took me a while to understand pointers and how to access data inside of a struct. but i recognize the difficulty you are having as it is similar to what i had and this is just the process i would go through to solve it without explicitly writing out the code for you...
if my understanding of this is off somewhere someone please let me know, it would be cool to get some feedback...

Related

Do dynamic structs need to be deleted if given value from a function?

Sorry if the question is hard to understand but I wasn't sure how to word it.
Say for example I had a struct like the following,
struct Days {
int counter;
};
And in main I had
Days *d = new Days;
d.counter = 0;
If I then use d in a loop like this inside main
do {
int num = rand()%900+1000;
d = add(d, num);
} while(user decides to continue}
delete d;
where the function add is as follows
Days *add(Days *d, int num) {
Days *temp2 = new Days;
temp2.counter = 0;
if (d.counter > 0)
temp2.counter = d.counter;
temp2.counter += num;
return temp2;
]
Do I need to delete d after each iteration of the do-while loop since its copying over?
If so, would it be in the add function in the if statement like this
if (d.counter > 0) {
temp2.counter = d.counter;
delete d;
}
Or would it be in the loop in main?
Actually, you don't need to allocate anything yourself.
In the main function, just declare your variable as:
// Create a Days object with counter initialized to zero
Days d {0};
Then, you can define your add function like that:
void add(Days & d, int num)
{
d.counter += num;
}
Or if you really want to use pointers instead of references:
void add(Days * d, int num)
{
d->counter += num;
}
Finally, you can just call it as follows:
do
{
int num = whatever();
add(d, num);
// add(&d, num); if you used the pointer version
}
while(condition);
If I may give you an advice, since add() is supposed to operate on a Days object, it may make sense to make it a member function of Days so that you wouldn't need to pass a Days as argument and get rid of the pointers/references stuff ^^
In that case,
add(d, num);
Would become:
d.add(num);

Can't pass class object array into function?

I am trying to use a function to calculate a grade average and store the letter grade into "letter". However, whenever I try to call the function, I get an error saying "no matching for call to "findAvg". I do not completely understand references and pointers. Is that the issue here? Any help and information is appreciated, thank you.
#include <iostream>
#include <fstream>
using namespace std;
class Student{
public:
double grades[4];
float avgGrade;
string letter;
string name;
};
void findAvg(Student f[]);
int main(){
Student students[3];
int i = 0;
fstream fin;
fin.open("input1.txt");
while(!fin.eof()){
fin >> students[i].name;
for (int j =0; j < 4;++j){
fin >> students[i].grades[j];
}
i += 1;
}
fin. close();
findAvg(students[3]);
cout << students[1].letter;
}
void findAvg(Student f[]){
for(int i = 0;i<3;++i){
f[i].avgGrade = ((f[i].grades[0] + f[i].grades[1] + f[i].grades[2] + f[i].grades[3]) /4);
if (f[i].avgGrade>=90){
f[i].letter = "A";
} else if (89>f[i].avgGrade && f[i].avgGrade<=80){
f[i].letter = "B";
} else if (79>f[i].avgGrade && f[i].avgGrade<=70){
f[i].letter = "C";
} else if (69>f[i].avgGrade && f[i].avgGrade<=60){
f[i].letter = "D";
} else {
f[i].letter = "F";
}
}
}
findAvg(students[3]);
should be
findAvg(students);
The idea that you reference the whole array, by using array[SIZE] (where SIZE is the size of the array) is a common newbie error. I guess it comes from a confusion between the array declaration and an expression. But declarations and expressions are not the same thing and different rules apply. In an expression array[n] always references an element of the array. And furthermore if n is the same as the size of the array then you are referencing an element that does not exist.
The strange thing is that you handle the arrays perfectly correctly in every other part of your code, students[i].grades[j]; for instance. But for some reason when you are calling the findAvg function you think different rules apply.
Try findAvg (students) instead of findAvg (students[3]). students[3] would give you the 4th student, which does not exist.
Your call findAvg(students[3]); is incorrect. You've defined a function that takes an array, but students[3] is a single Student object, not an array. It is also an error because it is trying to access an element outside the boundaries of the array
Try calling it as
findAvg(students);
Here you need to paas the object array
So
use this
findAvg(students);
instead of using
findAvg(students[3]);

Returning a string * type array from a function back into the main

I'm new to C++ and I am working on a function to shuffle strings
It takes an array of strings, shuffles them, and returns them back to the main.
I am returning a pointer to an array of strings called shuffled. The problem I have is that when I try to save that new pointer to the array to another pointer in the main, I start getting weird values that either reference to a file location in my computer or a bunch of numbers.
I'll post the entire code here but really what you want to look at is the return types, how I return it and how I save it in main. Please tell me why my pointer is not referencing the working array that is created in the function. Here's the code:
#include <cstdio>
#include <string>
#include <ctime>
#include <new>
#include <cstdlib>
using namespace std;
const char * getString(const char * theStrings[], unsigned int stringNum)
{
return theStrings[stringNum];
}
string * shuffleStrings(string theStrings[])
{
int sz = 0;
while(!theStrings[sz].empty())
{
sz++;
}
sz--;
int randList[sz];
for(int p = 0; p < sz; p++)
{
randList[p] = sz;
}
srand(time(0));//seed randomizer to current time in seconds
bool ordered = true;
while(ordered)
{
int countNumberInRandList = 0;//avoid having a sz-1 member list length (weird error I was getting)
for(int i = 0; i < sz; i++)
{
int count = 0;
int randNum = rand()%(sz+1);//get random mod-based on size
for(int u = 0; u < sz; u++)
{
if(randList[u] != randNum)
{
count++;
}
}
if(count == sz)
{
randList[i] = randNum;
countNumberInRandList++;
}
else
i--;
}
//check to see if order is same
int count2 = 0;
for(int p = 0; p < sz; p++)
{
if(randList[p] == p)
{
count2++;
}
}
if(count2 < sz-(sz/2) && countNumberInRandList == sz)
{
ordered = false;
}
}
string * shuffled[sz];
for(int r = 0; r < sz; r++) //getting random num, and str list pointer from passed in stringlist and setting that value at shuffled [ random ].
{
int randVal = randList[r];
string * strListPointer = &theStrings[r];
shuffled[randVal] = strListPointer;
}
for(int i = 0; i < sz; i++)
{
printf("element %d is %s\n", i, shuffled[i]->c_str());//correct values in a random order.
}
return *shuffled;
}
int main()
{
string theSt[] = {"a", "b", "pocahontas","cashee","rawr", "okc", "mexican", "alfredo"};
string * shuff = shuffleStrings(theSt);//if looped, you will get wrong values
return 0;
}
Strings allocate their own memory, no need to give them the "length" like you would have to do for char arrays. There are several issues with your code - without going into the details, here are a few working/non-working examples that will hopefully help you:
using std::string;
// Returns a string by value
string s1() {
return "hello"; // This implicitly creates a std::string
}
// Also returns a string by value
string s2() {
string s = "how are you";
return s;
}
// Returns a pointer to a string - the caller is responsible for deleting
string* s3() {
string* s = new string;
*s = "this is a string";
return s;
}
// Does not work - do not use!
string* this_does_not_work() {
string s = "i am another string";
// Here we are returning a pointer to a locally allocated string.
// The string will be destroyed when this function returns, and the
// pointer will point at some random memory, not a string!
// Do not do this!
return &s;
}
int main() {
string v1 = s1();
// ...do things with v1...
string v2 = s2();
// ...do things with v2...
string* v3 = s3();
// ...do things with v3...
// We now own v3 and have to deallocate it!
delete v3;
}
There are a bunch of things wrong here -- don't panic, this is what happens to most people when they are first wrapping their brains around pointers and arrays in C and C++. But it means it's hard to put a finger on a single error and say "this is it". So I'll point out a few things.
(But advance warning: You ask about the pointer being returned to main, your code does indeed do something wrong with that, and I am about to say a bunch of things about what's wrong and how to do better. But that is not actually responsible for the errors you're seeing.)
So, in shuffleStrings you're making an array of pointers-to-string (string * shuffled[]). You're asking shuffleStrings to return a single pointer-to-string (string *). Can you see that these don't match?
In C and C++, you can't actually pass arrays around and return them from functions. The behaviour you get when you try tends to be confusing to newcomers. You'll need to understand it at some point, but for now I'll just say: you shouldn't actually be making shuffleStrings try to return an array.
There are two better approaches. The first is to use not an array but a vector, a container type that exists in C++ but not in C. You can pass arrays around by value, and they will get copied as required. If you made shuffleStrings return a vector<string*> (and made the other necessary changes in shuffleStrings and main to use vectors instead of arrays), that could work.
vector<string *> shuffleStrings(...) {
// ... (set things up) ...
vector<string *> shuffled(sz);
// ... (fill shuffled appropriately) ...
return shuffled;
}
But that is liable to be inefficient, because your program is then having to copy a load of stuff around. (It mightn't be so bad in this case, because a smallish array of pointers isn't very large and because C++ compilers are sometimes able to figure out what you're doing in cases like this and avoid the copying; the details aren't important right now.)
The other approach is to make the array not in shuffleStrings but in main; to pass a pointer to that array (or to its first element, which turns out to be kinda equivalent) into shuffleStrings; and to make shuffleStrings then modify the contents of the array.
void shuffleStrings(string * shuffled[], ...) {
// ... (set things up) ...
// ... (fill shuffled appropriately) ...
}
int main(...) {
// ...
string * shuffled[sz];
shuffleStrings(shuffled, theSt);
// output strings (main is probably a neater place for this
// than shuffleStrings)
}
Having said all this, the problems that are causing your symptoms lie elsewhere, inside shuffleStrings -- after all, main in your code never actually uses the pointer it gets back from shuffleStrings.
So what's actually wrong? I haven't figured out exactly what your shuffling code is trying to do, but that is where I bet the problem lies. You are making this array of pointers-to-string, and then you are filling in some of its elements -- the ones corresponding to numbers in randList. But if the numbers in randList don't cover the full range of valid indices in shuffled, you will leave some of those pointers uninitialized, and they might point absolutely anywhere, and then asking for their c_strs could give you all kinds of nonsense. I expect that's where the problem lies.
Your problem has nothing to do with any of the stuff you are saying. As you are a beginner I would suggest not presuming that your code is correct. Instead I would suggest removing parts that are not believed to be problematic until you have nothing left but the problem.
If you do this, you should quickly discover that you are writing to invalid memory.
part two : you can't seem to decide on the type of what you are returning. Are you building a pointer to an array to return or are you returning an array of pointers.... you seem to switch between these intermittently.
part three : read #Gareth's answer, he explains about passing parameters around nicely for your instance.

Array elements of a class object are not being set correctly, set() and get() member functions likely cause

This is my first time working with classes in C++ and I seem to be getting tripped up quite a lot. My program is supposed to be a rewrite of a previous program that used struct (see here: Random number generator in a for loop gives same numbers each time), but using a class instead.
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
const int WHEEL_POSITIONS = 30;
const char wheelSymbols[WHEEL_POSITIONS + 1] = "-X-X-X-X-X=X=X=X*X*X*X*X#X#X7X";
class slotMachine
{
private:
int spinPos;
char spinSymbol;
public:
slotMachine(); // Constructor
char symbols[WHEEL_POSITIONS + 1]; // Should be private?
void setSpinSymbol(); // Spins the wheels
char getSpinSymbol() const // Returns the symbol
{ return spinSymbol; }
} wheels[3];
// Constructor initializes slot wheels to contents of wheelSymbols
slotMachine::slotMachine()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < (WHEEL_POSITIONS + 1); j++)
{
wheels[i].symbols[j] = wheelSymbols[j];
}
}
}
void slotMachine::setSpinSymbol()
{
for (int i = 0; i < 3; i++)
{
wheels[i].spinPos = (rand() % WHEEL_POSITIONS);
wheels[i].spinSymbol = wheels[i].symbols[(wheels[i].spinPos)];
}
}
void displayResults(slotMachine fwheels[3])
{
for (int i = 0; i < 3; i++)
{
cout << fwheels[i].getSpinSymbol();
}
}
void displayResults(slotMachine []);
//bool getWinner(slotMachine []);
int main(void)
{
slotMachine wheels[3];
time_t seed;
time(&seed);
srand(seed);
displayResults(wheels);
return 0;
}
The code compiles but outputs the following:
I have a feeling this error is caused by something having gone amiss in my constructor slotMachine, my getSpinSymbol() function, or my setSpinSymbol() function, but I've looked it over several times and can't seem to figure it out. I've read a handful of material online covering classes in C++, but I'm still very new and very shaky on the concept--apologies if it's something small or obvious that I've overlooked.
There are several issues with your code:
1.Class names should be started with upper case letter. slotMachine -> SlotMachine
2.Remove wheels[3] after class definition.You are using the array declared in main() method.
3.Why you are declaring displayResults(..) again after it's definition?
4.You are not calling setSpinSymbol() before displayResults(..).
The problem was explained to me by a friend not on StackOverflow, and I will transcribe his answer here in case anyone else (for any reason) runs into the same problem:
You aren't using constructors and methods correctly. You shouldn't be
accessing wheels (the array of slotMachine objects) directly inside
those methods; you should just be performing operations on "this," the
slotMachine object on which the method was called. For example, the
constructor slotMachine::slotMachine() is automatically called for
each element of the array wheels. You just need to initialize the
current slotMachine object inside the constructor:
slotMachine::slotMachine()
{
for (int j = 0; j < (WHEEL_POSITIONS + 1); j++)
{
this->symbols[j] = wheelSymbols[j];
}
}
And slotMachine::setSpinSymbol() should just set the value of
spinSymbol for the object on which the method was called:
void slotMachine::setSpinSymbol()
{
this->spinPos = (rand() % WHEEL_POSITIONS);
this->spinSymbol = symbols[this->spinPos];
}
(In all of this code, the this-> part is actually unnecessary; you
can leave it out if you want. I put it in to try to make it clearer
that these methods are operating on fields of "the current object.")
Now, the reason you are getting garbage is because you never call
setSpinSymbol(), so the spinSymbol field is never initialized in
these objects. You probably want to call setSpinSymbol() in the
constructor, so that the spinSymbol field is guaranteed to be
initialized.
This explanation did solve my problem, and my program now outputs the correct information, so I believe it to be correct. My issues with using constructors and methods correctly has been explained here, and the reason why I was getting garbage values (as well as a few other points) was answered by another commenter.

Dynamically Set Array Size C++

I don't know any C++ at all but I am trying to make a very small update to a C++ library that my application is using. Before I start hacking away at this, I am hoping someone can tell me the proper syntax for the following:
I have the following lines of code:
#define A_NUMBER 100
#define ANOTHER_NUMBER 150
enum {
type1,
type2,
};
static int someMethod(int type)
{
char command[A_NUMBER];
//...more code
}
What I need to be able to do is based on the type argument (type1 or type2) I need to be able to set the size of the array to be either A_NUMBER or ANOTHER_NUMBER.
In pseudo code it would be something like:
if (type == type1) {
char command [A_NUMBER]
}
else if (type == type2) {
char command [ANOTHER_NUMBER]
}
Is there a way to dynamically define the size?
Yes, you can use an std::vector<char>:
if (type == type1) {
std::vector<char> x(A_NUMBER);
} else if (type == type2) {
std::vector<char> x(ANOTHER_NUMBER);
}
Remember to include the header with:
#include <vector>
While your example code matches the "pseudo code" in the question, I think part of the question is how to decide the size via type and then use the resulting storage unconditionally, i.e. outside the conditional blocks.
Then it gets as simple as:
std::vector<char> x;
if (type == type1) {
x.resize(A_NUMBER);
} else if (type == type2) {
x.resize(ANOTHER_NUMBER);
}
I believe this is what you want
std::vector<char> x; // x is empty, with size 0
if (type == type1) {
x.resize(A_NUMBER); // change size to A_NUMBER
} else if (type == type2) {
x.resize(ANOTHER_NUMBER); // change size to ANOTHER_NUMBER
}
Yes and no. In standard C++, you cannot keep the array on the stack and have its size determined in runtime.
However, you can turn the array into a dynamically-allocated one (i.e. on the heap). In C++, the standard way to do this is to use std::vector:
std::vector<char> command(A_NUMBER); // or ANOTHER_NUMBER
Indexing will work just as before: command[5]
However, if you need to pass the array to something which expects a C-style array (i.e. a char *), you'll have to use one of these:
command.data(); // if your compiler supports C++11
&command[0]; // if it does not
And of course, to use std::vector, you'll have to #include <vector>.
Here's an example that works in C and C++:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
char *opt;
if(argc == 2) {
opt = *++argv;
}
else {
printf("Usage: %s [small|large]\n", *argv);
return EXIT_FAILURE;
}
int *arr;
int arrlen = 0;
if (strcmp(opt, "small") == 0) {
arrlen = 3;
arr = (int *) malloc(arrlen*sizeof(int));
int i;
for(i = 0; i < arrlen; i++)
arr[i] = i+1;
}
else if (strcmp(opt, "large") == 0) {
arrlen = 5;
arr = (int *) malloc(arrlen*sizeof(int));
int i;
for(i = 0; i < arrlen; i++)
arr[i] = i+1;
}
if (arrlen > 0) {
int i;
for(i = 0; arr[i]; i++)
printf("%i, ", arr[i]);
printf("\n");
free(arr);
}
return EXIT_SUCCESS;
}
Example:
[gyeh#gyeh stackoverflow]$ ./dynarr
Usage: ./dynarr [small|large]
[gyeh#gyeh stackoverflow]$ ./dynarr small
1, 2, 3,
[gyeh#gyeh stackoverflow]$ ./dynarr large
1, 2, 3, 4, 5,
The raw C++ way is new and delete
char * command = new char[A_NUMBER];
// and later delete it like this
delete[] command;
Of course you'll have to manage the memory, and it is not recommended to use this approach because of many reasons you should be able to find online. So in conclusion... don't use this method if vector is an option
If using a big array the best way would be to use C++ vector, you could even consider other data structures like list based on your needs (for example a lot of insert, deletions operations on your array).