array as function variable in C++

Hi all,

here is my code to pick a random element from an array.


int pickOne(int anArray[])
{
      const int nSize = (sizeof(anArray)/sizeof(anArray[0]));
      int nIndex = GetRand(0, nSize - 1);
      return anArray[nIndex];

Open in new window

}


and in the main, I have

      int anDigits[nSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
      cout << pickOne(anDigits) << endl;

I expect it pick a random digit every time, but it always return the first digit. Then I went back to check the function pickOne, it turns out that sizeof(anArray) return 4 in that function, which is supposed to be 36.

Why did it return 4 instead?

Please help.

Thanks,
RDB
ResourcefulDBAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

phoffricCommented:
You thought that you were passing the entire array to pickOne(), but you were just passing by value the address of the start of the array. You could pass the entire array if you wanted by including it in a struct and passing the struct by value; but this is not recommended since an arbitrary array size being passed by value could affect performance by doing useless copies onto the frame stack, as well as overflow the frame stack depending upon how large the array is.

>> it turns out that sizeof(anArray) return 4
anArray is the address of the start of the anDigits array. In your platform, it looks like the size needed to fit an address is 4 (i.e., 32-bit platform). So, nSize should be 1. anArray is effectively an int* pointer that points to the start of the anDigits array.

So, pass the length of the array as an argument as well as the array.

Not sure how your GetRand() function is defined. Unless it has some random entity in it like time (seconds or better, milliseconds), you may still get the same random index.
0
peprCommented:
To add to phoffric's comment, Bjarne Stroustrup (creator of C++) considers arrays as one of the most problematic C relicts. He recommends to use std::vector instead whenever you can. The vector contains methods for determinig its size, its capacity, allows you to use range check indexing, etc.
0
Julian HansenCommented:
As an alternative you could just do the random selection inline instead of calling a function to do it
cout << anDigits[GetRand(0, sizeof(anDigits)/sizeof(anDigits[0]))] << endl

Open in new window


Not sure what the modern take on Macro's is but you could also define a Macro like so

#define PICKONE(A) A[GetRand(0, sizeof(A)/sizeof(A[0]))]

Open in new window


And call it like so

cout << PICKONE(anDigits) << endl;

Open in new window


This will eliminate the issues of having to pass the array to a function.
0
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

sarabandeCommented:
You could pass the entire array if you wanted by including it in a struct and passing the struct by value; but this is not recommended since an arbitrary array size being passed by value could affect performance by doing useless copies onto the frame stack, as well as overflow the frame stack depending upon how large the array is.
for a fixed array with less than - say - 100 elements, the non-recommendation is no longer substantive, in my opinion. nevertheless you should follow the advice of pepr and use std::vector instead of a c array. you would pass it by reference, what also makes the possible stack issues non-relevant.

int pickOne(const std::vector<int> & anArray)
{
      const int nSize = (int)anArray.size();
      if nSize == 0) return -1;  // error handling
      // rest is same code 
      ....

...

std::vector<int> anArray;
loadArray(anArray);
int x = pickOne(anArray);

Open in new window


Sara
0
evilrixSenior Software Engineer (Avast)Commented:
Or, if it's meant to be fixed size use std::array.
http://www.cplusplus.com/reference/array/array/
0
ResourcefulDBAuthor Commented:
Thank all for comments. I still do not find a good way to cop with it.
To clarify a little. Here is what we have:

1. GetRand() is well functioned, it does have time() in there, it was tested seperately and did give different number every time. So, the question is still with how to get the size of an array when it is passed as an variable to a function.

2. I tried #define PICKONE(A) A[GetRand(0, sizeof(A)/sizeof(A[0]))] in Visual C++ 2012 and does not seem to work. Maybe we need something else.

3. The reason I want this to be in a stand alone function is,  reuseability, we may randomly pick one element out of an array many time in the main().

4. I think making array to vector can work out and follow sara's function declaration. In the main(), I used       std::vector<int> anDigits;  

to make anDigits an vector, but the pickOne is not happy to take it.

What could be the problem?

Please help.

Thanks,
RDB
0
Julian HansenCommented:
This is how I tested it.

#include "stdafx.h"
#include <time.h>
#include <iostream>

#define PICKONE(A) A[GetRand(0, sizeof(A)/sizeof(A[0]))]

using namespace std;

int GetRand(int min, int max)
{
	int seed = time(NULL);
	srand(seed);
	return  rand() % (max-min) + min;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int anDigits[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

	cout << PICKONE(anDigits) << endl;

	return 0;
}

Open in new window

0
evilrixSenior Software Engineer (Avast)Commented:
Generally, you can't get the size of an array from what is passed to the function because an array always (ALWAYS) decomposes to a pointer that references the address of the first element (Paul has already explained this). You can; however, use a bit of template trickery to get the array size.

#include <iostream>

template <size_t N>
void foo(int (&array) [N])
{
   std::cout << "size: " << N << std::endl;
}

int main()
{
   int array[100];
   foo(array);
}

Open in new window


What this does is implicitly pass the size of the array as a template argument. The function prototype, which looks a little odd, is just using N to define the parameter as a reference to an int array of size N. You can use N in your function to get the size of the array.

See http://www.cplusplus.com/articles/D4SGz8AR/ for more info.
0
peprCommented:
Firsly, +1 for std::array advice by evilrix. It depends on situation what is more suitable (i.e. std::vector vs. std::array).

You already wrote
... it turns out that sizeof(anArray) return 4 in that function
and that is the correct observation.

The problem is that the sizeof operator does not return for the array what you expect. As phofric already wrote above, when passing the array into a function, it is technically just passing the address of the first element of the array. The C-array is not a compact object, and it does not store the information about the number of elements. This is the problem. When working with a C-array, you have to know (i.e. store separately) its size.

If A is the array, then using A means "the address of the first element". For 32-bit application, the address is stored on 4 bytes. This way sizeof(A) is the 4 bytes. The A[0] is related to the element and it has its type. For int and a 32-bit application, it is stored on 4 bytes. This way sizeof(A[0]) will also return 4.

To summarize, if you want work with a C-array and pass it to a function that is expected to process it, you have to add another argument that says the number of element the array has. It cannot be avoided when working with C-array.

The related problem is that you have to pass the correct number of elements of the array; otherwise, you can easily index out of range of the array. This is another reason why using C-arrays is error prone.
0
sarabandeCommented:
I used       std::vector<int> anDigits;  
to make anDigits an vector, but the pickOne is not happy to take it.

did you add #include <vector> statement?

how did you make the initialization? it could be done like:
static int digs[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  // see the 0 
std::vector<int> anDigits(&digs[0], &digs[sizeof(digs)/sizeof(digs[0])]);

Open in new window

if the body of function pickOne is below the function code where pickOne was called, you need a declaration of function pickOne:
int pickOne(const std::vector<int>& anDigits);

Open in new window

also your sources should be compiled by c++ compiler and not by c compiler. normally, that is granted by choosing the right extension .cpp.

if nothing helps please post your code and the errors you get.
Sara
0
ResourcefulDBAuthor Commented:
julianH,

your code is working, I did not know it could be as simple as one line definition.

here is a related question. if we print out three picks, they all have the same answer, how do we make them all random in every pick?

		cout << PICKONE(anDigits) << endl;
		cout << PICKONE(anDigits) << endl;
		cout << PICKONE(anDigits) << endl;

Open in new window


Do we modify the rand function, change seed every time we call rand, etc? how do we do that?

Thanks,
RDB
0
Julian HansenCommented:
move the srand call out of the GetRand function into your main app i.e.

#include "stdafx.h"
#include <time.h>
#include <iostream>

#define PICKONE(A) A[GetRand(0, sizeof(A)/sizeof(A[0]))]

using namespace std;

int GetRand(int min, int max)
{
	return  rand() % (max-min) + min;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int anDigits[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
	int seed = time(NULL);
	srand(seed);

	cout << PICKONE(anDigits) << endl;

	return 0;
}

Open in new window

By reseeding the random number generator each time - your code is running too fast for time() to generate a different value on each call so it is seeding from the same number.

By moving the srand to the main function the seed will be different on each application run.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ResourcefulDBAuthor Commented:
julianH, thanks, that moving of srand() works.
#define work really well in this situation, by picking an item.

Now that if we want to randomly kick out an item in an array and return the rest, can #define work?

such as:
originalArray = {1, 2, 3, 4, 5, 6} and kick out the item in position 3, which is originalArray[3] = 4, and the result is {1, 2, 3, 5, 6}

I image a function like this, it takes two variables, the first is an array, the second is a position, such as:

kickOne(OriginalArray, 3)

and return another array, can #define be helpful here?

Thanks,
RDB
0
Julian HansenCommented:
That would be another question.

The solution would probably require a function but it would not pose the same problems as this one with respect to passing the array by reference. It would be quite involved though. When it comes to removing elements from arrays - so the remaining elements are contiguous (i.e. no blank boxes in between elements) - requires some work. You would have to recreate the array without the require element - and a number of things come into play - do you want to decrease the allocated space for the array or just move all items to the right of the selected element one place to the left (memmove) - if the latter the solution you can probably use a macro with memmove.

If you need to redeclare the array to be smaller then you would probably need to consider the std:vector solution
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.