1

I have to dynamically allocate an array and pass it to a function to calculate odds of a weighted die being rolled. Whenever I run through my code the function doesn't remember the values added to my array and returns random values, what's wrong about the way I'm passing *weight into the roll function? I added print statements after adding weights in and the weight is entered fine up until it's passed to the function via pointer.

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

int roll (int sides, double *weight) {
int total[sides + 1];
total[0] = 0;

for (int i = 1; i <= sides; i++) {
    total[i] = total[i - 1] + weight[i]; 

}


int current = (rand() % total[sides + 1] - 1 + 1);
//cout << current << " ";

for (int i = 1; i <= sides; i++) { // 1 to weight 1,,,,, weight 1 to weight 
2
    if (current <= total [i] && current > total[i - 1]) {
        current = i;
    }
}



return current;
}

Function that is supposed to retrieve the random number rolled. ^

int main () {

int sides = 0;
int rolls = 0;
int answer = 0;
int currentRoll = 0;
bool done = false;
double* weight;
double totalWeight;
srand(time(NULL));

cout << "Dice Roll Menu: " << endl << "1. Specify an output file" << endl << 
"2. Enter sides and weight for a die" << endl << "3. Simulate a specified 
number of rolls of the weighted die" << endl << "4. Exit" << endl;

while (done != true) {
    cout << endl << "Enter a number that corresponds to you choice: ";
    cin >> answer;


    while (answer == 2) { //SIDES
        cout << "Please enter the number of sides on the die (must be 
greater than two): ";
        cin >> sides;
        if (sides < 2) {
            cout << "Invalid input, try again." << endl;
        }
        else {
            weight = new double[sides + 1];
            for (int i = 0; i < sides + 1; i++) {
                weight[i] = 0;
            }
            break;
        }
    }


    while (answer == 2) {
        totalWeight = 0;
        for (int i = 1; i <= sides; i++) { //WEIGHT 
            cout << "Enter a weight for side " << i << ": ";
            cin >> weight[i];
            cout << "TEST: " << weight[i] << endl;
            totalWeight = weight[i] + totalWeight;

        if (weight[i] < 0) {
            cout << "Invalid input. Try again.";
            totalWeight -= weight[i];
            i--;
            }
        }
        break;
    }

Loop that determines sides and weight and dynamically allocates the array. ^

    while (answer == 3) {
        cout << "Enter the amount of rolls you would like to perform: ";
        cin >> rolls;
        if (rolls < 0) {
            cout << "Invalid input. Try again.";
        }
        else {
            else if (totalWeight == 0) {
                cout << "Please enter weights of the dice first!" << endl;
                answer = 1;
            }
            else {
                done = true;
                break;
            }
        }
    }

//ACTUAL CODE HERE
for (int i = 1; i <= rolls; i++) { //CALCULATES
    currentRoll = roll(sides, &weight[i]);
    cout << currentRoll << " ";
}


}
  • What is the reason why you don't use standard containers, e.g. `std::vector`? – bolov Oct 27 '18 at 01:25
  • 1
    `int total[sides + 1];` Note that this is not valid C++. To denote the number of entries in an array in C++, the number of entries is denoted with a compile-time constant. You are using an extension that is not officially part of the C++ language. Use `std::vector total(sides + 1)` instead. – PaulMcKenzie Oct 27 '18 at 01:26
  • Welcome to Stack Overflow. When you write code, it behooves you to implement new functions *in isolation* as much as possible, and not combine them until they work perfectly. You are trying to get a function to operate on an array; all of this stuff about user interaction, file I/O and random numbers is just obstructing the view. – Beta Oct 27 '18 at 01:45
  • Why are you passing `&weight[i]` to `roll`? Think about what this is doing, and what you want it to be doing. – 1201ProgramAlarm Oct 27 '18 at 01:46
  • @PaulMcKenzie , I have to be able to complete this program using dynamically allocated arrays, sadly not allowed to use vectors. – Tyler Zellers Oct 27 '18 at 01:48
  • @1201ProgramAlarm , I'm attempting to pass the exact weight value at weight[i], however when it gets to the function it changes, is there something wrong with the syntax? – Tyler Zellers Oct 27 '18 at 01:56
  • You will do yourself a favor if you make an effort to use natural 0-based array indexing, instead of twisting into contortions by allocating vectors with one extra element and using 1-based array indexing. You're fighting against the tide, it makes your code difficult to understand, and this kind of coding is fertile breeding ground for various bugs and one-off errors. In C, and C++, arrays and vectors use natural 0-based indexes. Make a mental effort to learn how to use 0-based indexes. You'll thank yourself for that, later. – Sam Varshavchik Oct 27 '18 at 01:57
  • You're passing a pointer to the `i`th element of `weight`. Since `roll` is different from `sides`, you're potentially accessing past the end of the memory you allocated for `weight`. Then in `roll` you access `weight[i]`, where i is positive, which also potentially exceeds the array bounds. – 1201ProgramAlarm Oct 27 '18 at 02:00
  • You think you're passing the address of an element; the function thinks it's receiving the address of the *first* element. – Beta Oct 27 '18 at 02:00
  • @1201ProgramAlarm What should I pass to the function instead of i so that it remains within it's boundaries? – Tyler Zellers Oct 27 '18 at 02:08
  • @TylerZellers -- The issue is that you're not using C++ to solve your problem. A dynamic array in C++ is done by either using `new[] / delete[]`, or using a container such as `std::vector`. What you're doing now is **not** C++, i.e. Variable Length Arrays (VLA's). I wished that gcc or whatever compiler you're using would turn these things *off* by default, so that new programmers don't fall into the trap you're falling in now of using them. – PaulMcKenzie Oct 27 '18 at 02:08
  • @Beta how do I pass the address properly? – Tyler Zellers Oct 27 '18 at 02:16
  • Let me translate "I have to be able to complete this program using dynamically allocated arrays, sadly not allowed to use vectors" ==> "We're not allowed to learn to program in C++". – Eljay Oct 27 '18 at 02:23
  • @Ejay -- Exactly my point. What the OP is learning is not C++. It is some other language, as that syntax is not C++. Even the `new[]` syntax would not be optimal, but at least it is C++. Doing things like `int whatever[n];` never has been part of the C++ language. – PaulMcKenzie Oct 27 '18 at 02:25
  • @PaulMcKenzie I'm really confused by your comment, I'm learning c++ at penn state using the "C++ From Control Structures through Objects" textbook and I'm using cloud9 to program it. int whatever[n] works completely fine for creating arrays, I'm just not efficient in using pointers correctly and would like some helpful insight. The program completely compiles correctly, it just passes the array incorrectly. – Tyler Zellers Oct 27 '18 at 02:31
  • Don't know simple it is to make this. The compiler you're using, `gcc`, has turned *on*, by default, the syntax you're using. The syntax you're using is *not*, I repeat, *not* C++. Your code is compiling only due to you not using the "compile ANSI C++ only" switch. Your code will fail to compile if ANSI C++ mode is turned on, and will totally fail to compile if you used, for example, the Visual Studio compiler. Bottom line -- that syntax is **not** C++. [See this topic](https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard) – PaulMcKenzie Oct 27 '18 at 02:37
  • @PaulMcKenzie okay, my bad. Do you have any insight on the correct way to use a pointer to point to the addresses of the array rather than the first element? – Tyler Zellers Oct 27 '18 at 03:01
  • When dealing with arrays, passing a pointer to the first element is all you need (plus a size argument). Another piece of advice is to always write small test programs, so you understand the basic concepts of what you're trying to accomplish. Something [like this](https://www.ideone.com/954fOp). – PaulMcKenzie Oct 27 '18 at 03:47
  • You must decide exactly what you are trying to do, before you worry about how to do it. I suggest you consider what you want the function to do, and then how, and then what it needs as an argument. – Beta Oct 28 '18 at 00:33

1 Answers1

1

Perhaps many of the misunderstandings that dominate the comments have to do with simply using C++ (and yet without using std::containers).

My out-of-the-box idea (or just plain crazy) is that there really is no conflict between:

  • "I have to be able to complete this program using 'dynamically allocated arrays', sadly I am not allowed to use vectors

  • yet all concerned seemed to agree that this is a C++ class assignment.


So, we need think of a way to create an array dynamically (I consider this part easy, not sure why). We want something with compile time fixed size. The array must exist in dynamic memory. (And no std containers.)

The goal has also been stated more simply

I have to dynamically allocate an array and pass it to a function to calculate odds of a ...

I propose the following. (This code compiles and runs. )

#include <iostream>
using std::cout, std::flush, std::endl;

// Step 1 - wrap an array inside a class
class Array_t
{
   const int  m_sz;
   int64_t*   m_arr;
public:
   Array_t()
      : m_sz(128)
      , m_arr (new int64_t[m_sz]) // allocate array in dynamic memory
      {
         // init for easy sum ... -------------v
         for (int j=0; j<m_sz; j+=1) m_arr[j] = 1; // easy sum
      }
   ~Array_t() = default;

   int64_t sum() {
      int64_t retVal = 0;
      for (int i=0; i<m_sz; i+=1)
         retVal += m_arr[i];  // direct access to the data!
      return retVal;
   }
};

// If your C++ 'Hello World' has no class ... why bother?

// Step 2 - auto var the above
class DUMY999_t
{
public:
   DUMY999_t() = default;
   ~DUMY999_t() = default;

   int operator()(int argc, char* argv[]) { return exec(argc, argv); }

private:

   int exec(int , char** )
      {
         // here is the array wrapped in a class, an automatic var!
         // the array is dynamically allocated in the class (see ctor)
         Array_t  arr;  
         // ctor provides the compile time constant

         // Step 3 
         // pass the array (in the class) to some function foo()
         cout << "\n  foo(arr) :" << foo(arr) << endl;

         // Step 4 - can we solve the 'how pass' question?
         // It should be obvious that foo is redundant ...
         // the following is both more direct
         // and object-oriented (a good thing)
         // >>> put the function in the object that has the data <<<    
         cout << "\n  arr.sum() :" << arr.sum() << endl;
         // invoke an object method which has
         // direct access to the data!

         return 0;
      }

   // why pass the data to the function? (c-style?)
   int64_t foo(Array_t& arr)
      {
         return arr.sum();
      }
   // why not install the function into the object? (c++?)

}; // class DUMY999_t

int main(int argc, char* argv[]) { return DUMY999_t()(argc, argv); }

Typical output:

  foo(arr) :128

  arr.sum() :128
2785528
  • 5,438
  • 2
  • 18
  • 20
  • Well thought out approach, but (syntax) you cannot use the *comma operator* with `using std::cout, std::flush, std::endl;` – David C. Rankin Oct 27 '18 at 06:33
  • @DavidC.Rankin - Thank you. I did not notice until you mentioned it that this feature is 'new-ish', and available only since '-std=c++17'. The comma list is called a "using declarator-list ;" with description: "A using-declaration with more than one using-declarator is equivalent to a corresponding sequence of using-declarations with one using-declarator." See https://en.cppreference.com/w/cpp/language/namespace. – 2785528 Oct 27 '18 at 21:16
  • Now that is something I didn't know. The day is good, I learned something new. Thank you. – David C. Rankin Oct 28 '18 at 00:36
  • It works too. Only strangeness is my executable size on Linux using c++17 is `~4500 bytes` larger (stripped) than the c++11 executable. That's `~32%` -- is that normal? – David C. Rankin Oct 28 '18 at 00:58
  • @DavidC.Rankin - I built a parallel code (-O3 and lots of options, my deliverable) and the build with "using declarator-list" is 16 bytes smaller. On a desktop, I seldom check for such things. What is normal, my friend? – 2785528 Oct 28 '18 at 16:51
  • I generally build with `-Ofast`, and when I move from standard-to-standard option, just my pedantic curiosity usually leads me to ask "I wonder what that did to the executable?", so I usually pick around a just take a cursory look. Here, with the `-std=c++11` and `strip -s` I ended up with an executable of `14376-bytes`. Switching to `-std=c++17` (with all other things equal), I ended up with an executable of `18936-bytes`. Important? No. Curious? Yes `:)` – David C. Rankin Oct 29 '18 at 02:34
  • @DavidC.Rankin - I used to do something similar for my embedded work, but new design embedded boards got so much memory around the millenia, and there was so much code to develop. Thanks for the name 'strip' and sharing your ideas. – 2785528 Oct 29 '18 at 16:26
  • I forgot to mention: sizeof(Array_t) reports 16 bytes. sizeof (Array_t::m_arr [128]) reports 8 bytes, and 128 * sizeof (int64_t) is 1024 bytes. – 2785528 Nov 13 '18 at 14:35
  • `sizeof (Array_t::m_arr [128])` It appears that `int64_t* m_arr;` is reporting the `sizeof (a_pointer)` since `m_arr (new int64_t[m_sz])` is originally a pointer to a block of memory having allocated storage type, `sizeof (Array_t::m_arr [128])` is simply a pointer to a collection of `128` of them. – David C. Rankin Nov 13 '18 at 14:55