0

I am trying to create a program that randomly generates the position of "ships". I'd like a structure to keep track of multiple aspects of the ships including their positions and an array to keep track of multiple ships.

The error I'm getting seems to be occurring in the first for loop within "main" at the line fleet[i] = ship_position (fleet[i], i); The error reads:

error: cannot convert 'ship_stats' to 'ship_stats*' for argument '1' to 'ship_stats ship_position(ship_stats*, int)'

Also, previously, I did not think the second "i" within the brackets of that line was necessary, but when I tried compiling without it the error I received was:

error: expected primary-expression before ']' token

#include <string>
#include <cstdlib>
#include <ctime>

using namespace std;

int rand_range (int low, int high) {
    return rand() % (high - low + 1) + low;
}

struct ship_stats {
    int x_coordinate;
    int y_coordinate;
    int weapon_power;
};

ship_stats fleet[5]; // Create fleet of 5 ships linked to the structure

ship_stats ship_position (ship_stats fleet[], int ship_num) {
    //Randomly generate a starting position for each ship
    int low_x = 0; //Set max and min ranges for ship position
    int high_x = 1024;
    int low_y = 0;
    int high_y = 768;

    fleet[ship_num].x_coordinate = rand_range (low_x, high_x);
    fleet[ship_num].y_coordinate = rand_range (low_y, high_y);
    return fleet[ship_num];
}

int main () {
    int num_ships = 5;

    for (int i = 0; i < num_ships; i++)
        fleet[i] = ship_position (fleet[i], i); // <-- error is here
}
mcjack32
  • 21
  • 2
  • 4
    Why are you declaring the `ship_stats fleet[5]` in global space then trying to pass it to a function? – m_callens Mar 04 '17 at 21:19
  • The error message is quite clear - you are trying pass in a single `ship_stats` while the function expects an array of them (which decay to pointers in C++) => `ship_position (fleet[i], i);` should be `ship_position (fleet, i);` – UnholySheep Mar 04 '17 at 21:19
  • I think you should look up more about arrays in C (and C++). Here is a good tutorial: https://www.tutorialspoint.com/cprogramming/c_arrays.htm (there is also another reference where an array as parameter is covered) – bracco23 Mar 04 '17 at 21:33

3 Answers3

0

The mechanical point you have been given is that if you want to pass "raw" arrays as parameters in C++ (or C), you can't say fleet[]. That will not compile, as you noted. But don't just put a random index value in the brackets--that gets you an array element, not the array itself.

It's a somewhat gross oversimplification to say you pass the "whole raw array" just by saying fleet. You're really passing the address of the first element, e.g. fleet ends up being equivalent to &fleet[0]. Crude though that is, it comes from C's heritage...and it means that if the function you're calling knows the size of the "raw" array, then that address can be meaningfully used as a means of accessing any subsequent value in it.

But in "idiomatic" C++, one rarely wants to use a "raw" C-style array. The std::vector solves several weak points of raw arrays, and is a good choice for beginners.

You can choose to pass vectors "by value" (which makes a copy) or "by reference" (which is a bit like passing by pointer, the only choice for a raw array). Vectors don't have to know their size in advance, and their size can change over the course of your program. Not only that, the size comes along with the vector when it's passed as a parameter. And very importantly, vectors speak an abstract "collection protocol", which means they're usable with #include <algorithm>.

But there are several other points. You have made a ship_position() function that "constructs" a ship. This oddly shaped function returns a value (the instance of a constructed ship), yet also mutates your "fleet" so that the ship is in that position. Why would you need to return the value and assign it to the location in the fleet...if that mutation of the fleet was already part of the function?

And in C++, the best mechanism for "constructing" something is...a constructor. ship_position() is what we would call a "free function"--it doesn't live within ship_stat's definition, yet it seems to have an eerie knowledge of how to manipulate ships.

(Supporting Note: Where does weapon_power fit into all this? Whose job is it to initialize it, or not? This is where a single point of responsibility of a "constructor" comes into place, to say that a constructed ship has all its fields covered.)

If this is to be a proper C++ program, you shouldn't think of ship_stats as a struct that is operated on by "free functions". Instead, you need to think of Ship as a class with some data, and some code. The randomization of a ship should be inside your class... the act of putting a random ship into a collection should be using push_back into a vector (or emplace_back, but that's very much skipping ahead).

Try not to use raw C arrays in C++ programs unless you really know what you're doing. (Or don't use C++ features and instead tag your question C, the answers will be very different.)

  • Thank you for such a detailed answer. The fix was simple (changing "fleet[]" to "fleet"). But going off of the other points you mentioned, I'm still a bit confused as to what you're saying is the proper way for my function to be written. For clarification, I'm working through an intro to C++ book and I'm only about a third of the way through (Pointers are next chapter). The author gave an example function EnemySpaceShip getNewEnemy () { EnemySpaceShip ship; ship.x_coordinate = 0; ship.y_coordinate = 0; ship.weapon_power = 20; return ship; } – mcjack32 Mar 06 '17 at 19:33
  • However, he did say that "This function will actually make a copy of the ship local variable that it returns. This means that it will copy every field of the structure into a new variable, one by one." but that this issue would be addressed in the next chapter. – mcjack32 Mar 06 '17 at 19:35
  • @mcjack32 There's a lot of variance in quality in C++ books, and it sounds like you might *(read: almost certainly)* need a better one. There's a good [book list here on StackOverflow](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), and I most recently was sent a review copy of the updated C++ Primer, which I thought was laid out pretty well. – HostileFork says dont trust SE Mar 06 '17 at 21:15
-1

Like already stated in the comments you are trying to pass one ship_stats to function thats waiting for an array of them , so for the function to work it should look like this

ship_stats ship_position (ship_stats fleet, int ship_num) {
    //Randomly generate a starting position for each ship
    int low_x = 0; //Set max and min ranges for ship position
    int high_x = 1024;
    int low_y = 0;
    int high_y = 768;

    fleet.x_coordinate = rand_range (low_x, high_x);
    fleet.y_coordinate = rand_range (low_y, high_y);
    return fleet;
}
BENS
  • 185
  • 2
  • 7
-1

Try

#include <string>
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;

int rand_range(int low, int high) {
return rand() % (high - low + 1) + low;
}

struct ship_stats {
int x_coordinate;
int y_coordinate;
int weapon_power;
};

ship_stats fleet[5]; // Create fleet of 5 ships linked to the structure

ship_stats ship_position(ship_stats IndividualShip, int ship_num) {
//Randomly generate a starting position for each ship
int low_x = 0; //Set max and min ranges for ship position
int high_x = 1024;
int low_y = 0;
int high_y = 768;

IndividualShip.x_coordinate = rand_range(low_x, high_x);
IndividualShip.y_coordinate = rand_range(low_y, high_y);


return IndividualShip;
}

int main() {
int num_ships = 5;



for (int i = 0; i < num_ships; i++) {
    fleet[i] = ship_position(fleet[i], i); // <-- error is here
}

}

In the above code, I've changed the function parameters of ship_position to accept an individual ship rather than the array as a whole. Your code should compile. Also, if this is C++ rather than C, consider using the for loop

for (ship_stats& j : fleet)
    std::cout << j.x_coordinate << " " << j.y_coordinate << endl;

to output the results of your program, using < iostream > of course.

fishermanhat
  • 195
  • 1
  • 8
  • While I appreciate the spirit of wishing to help, why would you take an individual ship (uninitialized) as a copied parameter, and then return a mutated version of that copy? Why correct code without removing an unused ship_num parameter? If you are going to mutate the ship, it should be passed by reference. It's much more important when trying to guide people from the C to C++ mindset to talk about constructors vs. syntactic sugar on `for` loops that does not address the problem. See [my answer](http://stackoverflow.com/a/42602465/211160), as you might benefit from it. – HostileFork says dont trust SE Mar 05 '17 at 00:26