I'm currently learning arrays and how they work. I'm given the following function that is used to find the maximum elements in three different arrays, A, B and C:
void findMax(int A[], int B[], int C[])
{
int maxA = A[0], maxB = B[0], maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(maxA < A[i]) maxA = A[i];
if(maxB < B[i]) maxB = B[i];
if(maxC < C[i]) maxC = C[i];
}
}
My goal is to figure out how to return all three values (maxA, maxB, maxC) without adding extra arguments. I'm allowed to change the return type and I can write code outside the function.
My first thought was to change void into int and create a new array that has those three max values. However, I think I need pointers to return arrays and we haven't learned that yet.
There are a few ways to approach this.
The Traditional Approach (Arrays)
The easiest way is to make the function return an int[]. This gives back all of the values easily and in a very concise format. However, the int[] type cannot guarantee that its size is 3 or that it contains what you claim it contains, so this is not a very type-friendly approach.
The OO Approach (Structs)
Most JAVA enthusiasts will tell you to make a struct or a class that encapsulates the idea, such as this.
struct MaxVals {
int maxA;
int maxB;
int maxC;
// Possibly a constructor here
};
This is still a very memory-efficient solution and is much cleaner than the array approach. It also has the benefit of being more type-safe; you can't make a MaxVals with four or two ints now; it's guaranteed at compile-time to have 3. However, this is bulky. It requires you to define a new type, completely outside of your function, that will only ever be used in this one case.
The Modern Approach (Tuples)
This is a new feature of C++, adapted from Python and Haskell, so it's only available in C++11, which has limited support across compilers. Tuples are a new data structure in the tuple header which can guarantee heterogeneous, fixed-size data structures of any types you specify at compile-time. You would set your return type to std::tuple<int, int, int> and initialize the structure with std::make_tuple(maxA, maxB, maxC).
This approach has the same memory advantages and guarantees as the struct approach but without all the overhead and boilerplate of declaring a one-use type. This would be the ideal approach for a problem like this, if your compiler supports it.
There are two things, you can do:
The first is declare maxA, maxB, maxC out side the function (globally). Then access them in your findMax function and main function.
int maxA, maxB, maxC;
void findMax(int A[], int B[], int C[])
{
maxA = A[0], maxB = B[0], maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(maxA < A[i]) maxA = A[i];
if(maxB < B[i]) maxB = B[i];
if(maxC < C[i]) maxC = C[i];
}
}
The second is use structure.
struct node
{
int maxA,maxB,maxC;
};
struct node findMax(int A[], int B[], int C[])
{
struct node Max_node;
Max_node.maxA = A[0], Max_node.maxB = B[0], Max_node.maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(Max_node.maxA < A[i]) Max_node.maxA = A[i];
if(Max_node.maxB < B[i]) Max_node.maxB = B[i];
if(Max_node.maxC < C[i]) Max_node.maxC = C[i];
}
return Max_node;
}
P.S: you also could use pointers, but as you said you are unfamiliar with them, I used other ways.
Related
I implemented a mergesort algorithm but it returns the exact same array I pass as an input. The following is the code. I am suspecting the pseudocode that our professor gave us is wrong. But I am not sure. I have tried to implement is as best as I can.
int len(double *a) {
int count = 0;
while (a[count] != '\0') {
count++;
}
return count;
}
double* merge(double* b, double* c, int N) {
int i = 0;
int j = 0;
double* result = new double[N];
for(int k = 0; k < N; k++) {
if ((i < len(b)) && (j >= len(c) || b[i] <= c[j])) {
result[k] = b[i++];
} else {
result[k] = c[j++];
}
}
return result;
}
void merge_sort(double* a, int N) {
if (N >= 2) {
int mid = (N+1)/2;
double *left = new double[mid];
double *right = new double[mid];
for (int i = 0; i < mid; i++) {
left[i] = a[i];
}
for (int j = 0; j < mid; j++) {
right[j] = a[mid + j];
}
merge_sort(left, mid);
merge_sort(right, mid);
a = merge(left, right, N);
}
}
Any help would be really appreciated.
In the last line, you assign your result to the local var a, which is then lost. You need to return a, or pass the input as a reference/pointer, otherwise any changes are only to the local copy.
Arguments in the function are basically local variables, they behave like any local variable in this function, except their initial value is set by code that calls this function. a is a pointer that stores the address of first element of your double array.
As it's a local variable, you can modify it but when the function ends, it will be discarded like all other local variables of the function.
There are several ways to deal with this problem, each with their own up and downsides. The most obvious is to return final value of a when you're done sorting. You could also pass a pointer TO a pointer to this function, and then you would be able to modify the pointer outside the function:
void function(int** argument){
*argument = another_function();
}
, but that severely restricts the source of your input. It no longer could be a local array passed by address like this:
int x = 10;
int *y = &x; // if this is what you want to change
function(&y); // this works
// now x is still 10, y points to a different place in memory which can store a different value
int x[1]; // if you would like to change this array in place though...
function(x); // this is how you would call the function, but it would fail because it can't change the address that x refers to
You main issue is here:
// You pass in a pointer to the data here.
// the parameter `a` holds a pointer to the data.
void merge_sort(double* a, int N) {
if (N >= 2) {
// STUFF
// Here you write over `a` (which is fine)
// BUT: You don't pass the value back.
// So the caller never knows what the new value is.
a = merge(left, right, N);
}
}
To fix this. I think it is a mistake to allocate a new array in merge(). Rather re-use the array you have. You have already copied the data into left and right to be sorted. The merge should merge the data back into the original array a.
// Change this:
a = merge(left, right, N);
into
merge(a, left, right, N);
Now in merge() you can use a as the destination.
void merge(double* result, double* b, double* c, int N)
// No longer need to allocate space for result now.
There are a couple of other issues:
1: What do you need len() for?
int len(double *a) {
int count = 0;
while (a[count] != '\0') {
count++;
}
return count;
}
You should already know the length of all parts you should not be re-measuring it. Also this function is completely wrong (the double array is not \0 terminated).
2: The length of b and c is not obvious.
double* merge(double* b, double* c, int N) {
You get the wrong value because you call len() which is not correct.
You could calculate from N but that has issues in that you need to make sure both you merge functions use exactly the same method and that is error prone in the long run. I would personally pass the size of each array as parameters to the function.
3: You leak your intermediate arrays!
You call new to allocate storage.
double *left = new double[mid];
double *right = new double[mid];
But you don't deallocate these objects so they are leaked (for every call to new there should be a corespodning call to delete).
Overall. You can solve a cople of issues by using more C++ style techniques (rather than the C style you are using). Iterators and std::vector spring to mind.
I'm trying to get a distinct value from my enumeration using void pointers.
I've an enum declaration of a list of animals
enum Animal {Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Sheep, Monkey, Rooster, Dog, Pig};
So firstly, i have a function that returns me a value from my enumeration randomly
VoidPtr getAnAnimal()
{
VoidPtr anAnimal;
Animal *a = new Animal;
int k = rand() % 12;
*a = static_cast<Animal>(k);
anAnimal = a;
return anAnimal;
}
Then during my construction of array, I've a conditional statement that states if the array has the same value, it is suppose to randomly generate another enum value
void constructSet(VoidPtr* animalArray, int size)
{
for(int i = 0; i < size; i++)
{
animalArray[i] = getAnAnimal();
int k = 0;
while ((k < i) && (animalArray[i] == animalArray[k]))
{
animalArray[i] = getAnAnimal();
k++;
}
}
}
Unfortunately, it still returns me the same enum value despite calling for another value if the array are the same.
Your error is that when comparing animalArray[i]==animalArray[k] you compare the addresses of the enums not their value. The proper comparison would be (edited after comment, obviously this is not what clean code looks like, but the mistake lies in passing the enum as a void*)
*static_cast<Animal*>(animalArray[i])==*static_cast<Animal*>(animalArray[k])
However, you should reconsider storing your Animal as pointer.
In C++ there is usually no reason to allocate objects by new (neither do you need to work with pointers in C++, unless you really have to). At least not for simple problems as yours.
Your algorithm in constructSet does not really correspond to your description.
I've a conditional statement that states if the array has the same
value, it is suppose to randomly generate another enum value
The two instructions animalArray[i] = getAnimal(); and k++ should be in different branches of a condition. k++ should iterate while the animalArray[i] is different from animalArray[k]. Suppose for example that *animalArray[i] != *animalArray[0] but *animalArray[i] == *animalArray[1] you will insert it even if the array has a same value.
Moreover k should be reinitialized each time a new animal is inserted.
Here is a possible alternative algorithm. I have not checked for the compilation errors.
void constructSet(VoidPtr* animalArray, int size)
{
int attempt = 0;
for(int i = 0; i < size; i++)
{
animalArray[i] = getAnAnimal();
int k = 0;
while ((k < i) && (*reinterpret_cast<const Animal*>(animalArray[i]) != *reinterpret_cast<const Animal*>(animalArray[k])))
k++;
if (k < i && attempt < 12) { // retry?
--i;
++attempt;
}
else // accept the animal
attempt = 0;
}
}
I needed to swap the contents of two std::string arrays of different sizes. Using std::vector<std::string> to use the swap vector method was the answer for other questions like this one, but using a vector was undesirable in my situation, since the swap is the only moment the arrays might have had their lengths changed - this function would only be called once, at initialization, other than that they'd be (very big) fixed length arrays. At the end I re-structured my code to avoid the swap in the first place, but I'm curious if it's feasible. I tried the following, where n is the length of a and m the length of b, but it segfaults.
void swap_strarr(std::string *a, std::string *b, int n, int m) {
int i;
std::string *tmp;
tmp = new std::string[n];
for (i = 0; i < n; i++) {
tmp[i] = a[i];
}
delete []a;
a = new std::string[m];
for (i = 0; i < m; i++) {
a[i] = b[i];
}
delete []b;
b = new std::string[n];
for (i = 0; i < n; i++) {
b[i] = tmp[i];
}
delete []tmp;
}
I first tried without the deletes on a and b. I also tried using std::string**, which shouldn't even work - and it didn't.
So... is it possible? Or, had I done the swap, should I just use std::vector even though they're fixed size arrays (apart from the swap) and a few million elements long?
Why not simply something like:
std::string* tmp = a;
a = b;
b = tmp;
or even simpler (thanks juanchopanza):
std::swap(a, b);
I originally asked using nested std::array to create an multidimensional array without knowing dimensions or extents until runtime but this had The XY Problem of trying to accomplish it with std::array.
The questions One-line initialiser for Boost.MultiArray and How do I make a multidimensional array of undetermined size a member of a class in c++? and their answers give some helpful information how to use Boost::MultiArray to avoid needing to know the extents of the dimensions at runtime, but fail to demonstrate how to have a class member that can store an array (created at runtime) whose dimensions and extents are not known until runtime.
Just avoid multidimensional arrays:
template<typename T>
class Matrix
{
public:
Matrix(unsigned m, unsigned n)
: n(n), data(m * n)
{}
T& operator ()(unsigned i, unsigned j) {
return data[ i * n + j ];
}
private:
unsigned n;
std::vector<T> data;
};
int main()
{
Matrix<int> m(3, 5);
m(0, 0) = 0;
// ...
return 0;
}
A 3D access (in a proper 3D matrix) would be:
T& operator ()(unsigned i, unsigned j, unsigned k) {
// Please optimize this (See #Alexandre C)
return data[ i*m*n + j*n + k ];
}
Getting arbitrary dimensions and extent would follow the scheme and add overloads (and dimensional/extent information) and/or take advantage of variadic templates.
Having a lot of dimensions you may avoid above (even in C++11) and replace the arguments by a std::vector. Eg: T& operator(std::vector indices).
Each dimension (besides the last) would have an extend stored in a vector n (as the first dimension in the 2D example above).
Yes. with a single pointer member.
A n multidimensional array is actually a pointer. so you can alocate a dynamic n array and with casting, and put this array in the member pointer.
In your class should be something like this
int * holder;
void setHolder(int* anyArray){
holder = anyArray;
}
use:
int *** multy = new int[2][1][56];
yourClass.setHolder((int*)multy);
You can solve the problem in at least two ways, depending on your preferences. First of all - you don't need the Boost library, and you can do it yourself.
class array{
unsigned int dimNumber;
vector<unsigned int> dimSizes;
float *array;
array(const unsigned int dimNumber, ...){
va_list arguments;
va_start(arguments,dimNumber);
this->dimNumber = dimNumber;
unsigned int totalSize = 1;
for(unsigned int i=0;i<dimNumber;i++)
{
dimSizes.push_back(va_arg(arguments,double));
totalSize *= dimSizes[dimSizes.size()-1];
}
va_end(arguments);
array = new float[totalSize];
};
float getElement(unsigned int dimNumber, ...){
va_list arguments;
va_start(arguments,dimNumber);
unsgned int elementPos = 0, dimAdd = 1;
for(unsigned int i=0;i<dimNumber;i++)
{
unsigned int val = va_arg(arguments,double);
elementPos += dimAdd * val;
dimAdd *= dimsizes[i];
}
return array[elementPos]
};
};
Setting an element value would be the same, you will just have to specify the new value. Of course you can use any type you want, not just float... and of course remember to delete[] the array in the destructor.
I haven't tested the code (just wrote it straight down here from memory), so there can be some problems with calculating the position, but I'm sure you'll fix them if you encounter them. This code should give you the general idea.
The second way would be to create a dimension class, which would store a vector<dimension*> which would store sub-dimensions. But that's a bit complicated and too long to write down here.
Instead of a multidimensional array you could use a 1D-array with an equal amount of indices. I could not test this code, but I hope it will work or give you an idea of how to solve your problem. You should remember that arrays, which do not have a constant length from the time of being compiled, should be allocated via malloc() or your code might not run on other computers.
(Maybe you should create a class array for the code below)
#include <malloc.h>
int* IndexOffset; //Array which contains how many indices need to be skipped per dimension
int DimAmount; //Amount of dimensions
int SizeOfArray = 1; //Amount of indices of the array
void AllocateArray(int* output, //pointer to the array which will be allocated
int* dimLengths, //Amount of indices for each dimension: {1D, 2D, 3D,..., nD}
int dimCount){ //Length of the array above
DimAmount = dimCount;
int* IndexOffset = (int*) malloc(sizeof(int) * dimCount);
int temp = 1;
for(int i = 0; i < dimCount; i++){
temp = temp * dimLengths[i];
IndexOffset[i] = temp;
}
for(int i = 0; i < dimCount; i++){
SizeOfArray = SizeOfArray * dimLengths[i];
}
output = (int*)malloc(sizeof(int) * SizeOfArray);
}
To get an index use this:
int getArrayIndex(int* coordinates //Coordinates of the wished index as an array (like dimLengths)
){
int index;
int temp = coordinates[0];
for(int i = 1; i < DimAmount; i++){
temp = temp + IndexOffset[i-1] * coordinates[i];
}
index = temp;
return index;
}
Remember to free() your array as soon as you do not need it anymore:
for(int i = 0; i < SizeOfArray; i++){
free(output[i]);
}
free(output);
The function cannot initialize an array because sizeof() returns bytes of an int pointer
not the size the memory pointed by myArray.
void assignArray(int *myArray)
{
for(int k = 0; k < sizeof(myArray); ++k)
{
myArray[k] = k;
}
}
Are there other problems ?
Thanks
Well no, there are no other problems. The problem you stated is the only thing stopping you from initialising the array.
Typically, this is solved by simply passing the size along with the pointer:
void assignArray(int* myArray, std::size_t mySize)
{
for (std::size_t k = 0; k < mySize; ++k)
myArray[k] = k;
}
Note that I've used std::size_t for the size because that is the standard type for storing sizes (it will be 8 bytes of 64-bit machines, whereas int usually isn't).
In some cases, if the size is known statically, then you can use a template:
template <std::size_t Size>
void assignArray(int (&myArray)[Size])
{
for (std::size_t k = 0; k < Size; ++k)
myArray[k] = k;
}
However, this only works with arrays, not pointers to allocated arrays.
int array1[1000];
int* array2 = new int[1000];
assignArray(array1); // works
assignArray(array2); // error
I don't see other problems. However, you probably wanted this:
template<int sz>
void assignArray(int (&myArray)[sz])
{
for(int k = 0; k < sz; ++k)
{
myArray[k] = k;
}
}
Unless, of course, even the compiler doens't know how big it is at compile time. In which case you have to pass a size explicitly.
void assignArray(int* myArray, size_t sz)
{
for(int k = 0; k < sz; ++k)
{
myArray[k] = k;
}
}
If you don't know the size, you have a design error.
http://codepad.org/Sj2D6uWz
There are two types of arrays you should be able to distinguish. One looks like this:
type name[count];
This array is of type type[count] which is a different type for each count. Although it is convertable to type *, it is different. One difference is that sizeof(name) gives you count*sizeof(type)
The other type of array looks like this:
type *name;
Which is basically just a pointer that you could initialize with an array for example with malloc or new. The type of this variable is type * and as you can see, there are no count informations in the type. Therefore, sizeof(name) gives you the size of a pointer in your computer, for example 4 or 8 bytes.
Why are these two sizeofs different, you ask? Because sizeof is evaluated at compile time. Consider the following code:
int n;
cin >> n;
type *name = new type[n];
Now, when you say sizeof(name), the compiler can't know the possible future value of n. Therefore, it can't compute sizeof(name) as the real size of the array. Besides, the name pointer might not even point to an array!
What should you do, you ask? Simple. Keep the size of the array in a variable and drag it around where ever you take the array. So in your case it would be like this:
void assignArray(int *myArray, int size)
{
for(int k = 0; k < size; ++k)
{
myArray[k] = k;
}
}