Gabriel M.
asked 10/20/21Write a C++ program with the following:
- expandArr(arr,size) , a function which accepts an array parameter and expands the array by 50%. The function deallocates any old dynamic memory and returns the newly created array.i.e., newSize = oldSize * 1.5
- getMax, a function which accepts an array parameter and returns the largest element in the array
- getAvg, , a function which accepts an array parameter and returns the average of all elements.
- main, performs the following:
- Create a dynamic array of size 5 and type double
- Prompt the user to enter values into the array. The prompt should continue until a negative number is entered
- During the entry process, if the array is filled, expand the array by calling the expandArr() function
- Display the maximum value in the array by calling getMax()
- Display the average values in the array by calling getAvg()
Sample program execution:
Enter grade: 26
Enter grade: 99
Enter grade: 5
Enter grade: 0
Enter grade: 67
Enter grade: 79
Enter grade: 55
Enter grade: -1
Max: 99
Avg: 47.2857
1 Expert Answer
Dayaan M. answered 22d
Bachelor's in Computer Science, Beginner Friendly Programming Tutor
The three functions are short, so I will give you all of them, then spend most of this on the two things in this spec that break people's code. One is a pointer problem that makes the whole thing crash, the other is a hidden bug in "expand by 50 percent" that only shows up at small sizes. I compiled and ran everything below before posting it.
expandArr, and the detail that makes or breaks it.
void expandArr(double *&arr, int &size) {
int newSize = size * 1.5;
if (newSize <= size) newSize = size + 1; // guard, see below
double *bigger = new double[newSize];
for (int i = 0; i < size; i++) bigger[i] = arr[i];
delete [] arr;
arr = bigger;
size = newSize;
}
Look hard at the parameter list. It is double *&arr, a reference to a pointer, not double *arr. This is the part to get right.
If you write double *arr, the function receives a copy of the pointer. You allocate the new block, copy the values, delete the old block, and set your local copy to point at the new one. Then the function returns, your copy is destroyed, and main's pointer still holds the address of the block you just deleted. Every read after that is use after free. The nasty part is that it often appears to work, because the freed memory usually still contains your old values, so the bug surfaces later and somewhere else. The ampersand is what lets the function repoint the caller's own pointer. Same reasoning for int &size, since the caller has to learn the new capacity.
If your instructor requires a plain pointer parameter, then return the new one instead and have main reassign: arr = expandArr(arr, size);. What you must not do is take a plain pointer and return void.
getMax and getAvg. Both take the element count, not the capacity. That distinction matters and I will come back to it.
double getMax(const double *arr, int count) {
double m = arr[0];
for (int i = 1; i < count; i++) if (arr[i] > m) m = arr[i];
return m;
}
double getAvg(const double *arr, int count) {
double sum = 0.0;
for (int i = 0; i < count; i++) sum += arr[i];
return sum / count;
}
Start getMax at arr[0] and the loop at 1, not at 0 with m initialized to 0. If you seed it with 0 and every value entered is negative, you will report a maximum of 0, which is not in the array at all. Seeding from the first real element cannot be wrong. The const is there because neither function modifies anything, and it lets the compiler catch you if you ever accidentally assign through the pointer.
main, with the capacity and count kept separate.
int size = 5, count = 0;
double *arr = new double[size];
double v;
cout << "Enter a number (negative to stop): ";
while (cin >> v && v >= 0) {
if (count == size) expandArr(arr, size);
arr[count++] = v;
cout << "Enter a number (negative to stop): ";
}
if (count == 0) { cout << "No numbers entered.\n"; delete [] arr; return 0; }
cout << "Max: " << getMax(arr, count) << "\n";
cout << "Average: " << getAvg(arr, count) << "\n";
delete [] arr;
Two variables, two different jobs. size is how many slots you own, count is how many you have filled. Collapsing them into one is the most common way this assignment goes wrong. If you pass size to getAvg instead of count, you divide by the capacity, so the unfilled slots drag your average toward zero and the answer is quietly wrong rather than obviously broken.
The expand check goes before the write, and tests count == size, meaning every slot is full. Writing first and expanding after puts one element past the end of the block.
Note cin >> v inside the condition. That handles the user typing a letter or hitting end of file, which otherwise leaves v unchanged and spins forever. And the count == 0 check matters because if the very first entry is negative, getMax reads arr[0] of an empty array and getAvg divides by zero.
Verification. Compiled with g++ -std=c++17 -Wall -Wextra, no warnings. Feeding it 1 through 12 then -1 gives count 12, capacity 15, max 12.00, average 6.50, which is correct since 1 through 12 sums to 78 and 78/12 is 6.5. A single value then -1 gives count 1, capacity 5, no expansion, max and average both 7.00. An immediate -5 prints "No numbers entered" and exits cleanly. Valgrind reports 0 bytes in use at exit and 0 errors.
Now the bug hiding in "expand by 50 percent". You wrote newSize = oldSize * 1.5, and oldSize is an int while 1.5 is a double. C++ promotes oldSize to double, multiplies, then truncates on assignment back to int. It does not round. Here is the actual growth chain I measured starting from 5:
5 → 7, 7 → 10, 10 → 15, 15 → 22, 22 → 33, 33 → 49, 49 → 73, 73 → 109
Note the first step. 5 times 1.5 is 7.5, and you get 7, not 8. That is fine, just be aware your capacities will not look like textbook numbers, and that is why my test showed capacity 15 rather than 16 after 12 insertions.
The real problem is at size 1. One times 1.5 is 1.5, which truncates to 1. The array never grows. I traced it and from size 1 the chain is 1 → 1 → 1 → 1, forever. So expandArr allocates a new block of exactly the same size, copies, frees, and returns with nothing gained, and the caller immediately finds the array full again. Depending on how your main is written, that is either an infinite loop or a write past the end.
Your spec starts at 5 so you will not hit it as written, but it is one edit away from biting you, and it is the kind of thing a grader probes. That is what the if (newSize <= size) newSize = size + 1; line prevents. From size 2 the chain is fine, 2 → 3 → 4 → 6 → 9, so 1 is the only broken case, which is exactly why it survives casual testing.
A cleaner way to say the same thing is int newSize = size + size / 2 + 1;, which uses only integer arithmetic and is always strictly larger. Or newSize = size * 3 / 2 + 1. Avoiding the double entirely sidesteps any question about rounding.
Why 50 percent growth rather than adding a fixed amount. This is worth understanding because it is the actual lesson behind the assignment. If you grew by adding 1 slot each time, then inserting n elements copies 1 + 2 + 3 + ... elements in total, which is n squared over 2 work. Growing by a constant factor makes the copies geometric instead, so the total copying is proportional to n and each insertion averages out to constant time even though individual insertions occasionally do a big copy. That is called amortized constant time, and it is exactly how std::vector works internally. Your assignment is making you hand build a vector so that push_back stops being magic.
Which brings up the thing worth saying plainly: in real C++ you would write std::vector<double> v; v.push_back(x); and never touch new, delete, or a capacity variable. The point of doing it manually once is that the memory ownership becomes concrete. Every new[] needs exactly one matching delete[], square brackets on both, and you have to know who owns the pointer at each moment. Get that straight here and vector will make sense for the rest of the time you write C++.
Still looking for help? Get the right answer, fast.
Get a free answer to a quick problem.
Most questions answered within 4 hours.
OR
Choose an expert and meet online. No packages or subscriptions, pay only for the time you need.
Tom O.
10/24/21