Crazy_Boy53
Crazy_Boy53

Reputation: 241

Why do we specify arrays size as a parameter when passing to function in C++?

I searched this question, most of them says the same thing. Since we only pass the arrays address in a function, compiler can not know the arrays size by looking at the address, they say. I tried to test this by using this code, and both functions gave the same results. So, how does specifying the arrays size as a function parameter help me in a practical way?. In which conditions does specifying the size help us?.

class ArrayTest
{
    public:
     void say(int ar[])
     {
         cout<<ar[1]<<endl;
         cout<<ar[7]<<endl;
     }

      void say(int ar[],int sizeAn)
     {
         cout<<ar[1]<<endl;
         cout<<ar[7]<<endl;
     }

};

int main()
{
    ArrayTest test;
   int anAr[5] = {1,2,3,4,5};
   test.say(anAr);
   test.say(anAr,5);
    return 0;

}

Upvotes: 5

Views: 12352

Answers (5)

dev
dev

Reputation: 257

Passing array size as a function parameter is a bad idea, because if you need an array as an array in function passing its size won't have any effect. The array you passed will be decayed to a pointer. So you need to maintain array as is.

Templates provide a simple and effective way to prevent array decay while passing them as function arguments.

template<std::size_t N>
void foo(int (&your_array)[N])
{
   for(int i = 0; i < N; i++)
      //process array, N will be your array size.
}
//simply pass array when calling the function. N be taken automatically.

//somewhere else
int main()
{
  int arr[10];
  foo(arr);
}

hope this helps.

Upvotes: 4

Deduplicator
Deduplicator

Reputation: 45654

Why do we specify arrays size as a parameter when passing to function in C++?

Do we?
Well, sometimes. The canonical way to pass a range in C++ is using an iterator-pair though, even if I can see it evolve to using ranges when the Range-TS is finally used everywhere.

Anyway, there are other ways to convey what (sub-)range we want to work with. So, let's take a look:

  1. In-band-signalling, like NUL-terminator for c-strings.
  2. An implicit part of the functions contract, like "it will always be exactly 12 elements".
  3. Passing a view of the part we want. Unfortunately, until the ranges-TS is fully incorporated, standard-library-support for that is severely anemic, being restricted to std::string_view in C++17 and extended with std::span for contiguous ranges (like arrays) in C++20 (look at the guideline-support-library for now).
  4. Using an iterator-pair. The full flexibility of iterators, though calculating the length might be costly, or impossible without consuming the range. This is the preferred way in the standard-library.
  5. Using start-iterator and length. Also quite common, but not to the same degree, and does not allow iterators determining the length as you iterate, not that that is an issue here.
  6. Using a (constant where appropriate) reference to the whole container or range, probably templated for generality. This might be combined with point 3, but need not.

Of those, if you know the element-type, and restrict to contiguous arrays, pointer+length is the most comfortable and flexible to use for now, which does not need different code for different lengths, so that's that.

Upvotes: 2

PeterT
PeterT

Reputation: 8284

This is about you as a programmer having the chance to boundary check, not whether the compiler can do it.

Just try to print out all the elements in the array, with the size:

 void say(int ar[],int sizeAn)
 {
     for(int i=0; i< sizeAn; ++i)
         cout<<ar[i]<<endl;
 }

now without the size:

 void say(int ar[])
 {
     for(int i=0; i< /*HOW DO I KNOW NOW?*/; ++i)
         cout<<ar[i]<<endl;
 }

Upvotes: 11

Neil
Neil

Reputation: 11889

As you say, compilers can't tell how big an array is if passed to a function. Your first say function tries to reference past the end of the array (ar[7] is beyond the size of 5). Your second say function means you can length check to make sure you don't make this error.

  void say(int ar[], int sizeAn) 
  {
     if(sizeAn>1)
        cout<<ar[1];endl;
     if(sizeAn>7)
        cout<<ar[7];endl;
  }

This way, YOU know the length and the function can check it before accessing invalid memory locations.

Upvotes: 2

Blaze
Blaze

Reputation: 16876

Note that your code is invoking undefined behavior because you're accessing element 7 of an array that is only 5 elements big. Using the size parameter, you could for instance check if the index is past its size and not do that call instead.

In your example, you get the same results becaue you aren't actually using the parameter:

 void say(int ar[],int sizeAn)
 {
     cout<<ar[1]<<endl;
     cout<<ar[7]<<endl;
 }

sizeAn is unused, so it's not making any difference. But consider for instance the following code:

void say(int ar[],int sizeAn)
     {
         for (int i = 0; i < sizeAn; i++){
             cout<<ar[i]<<endl;
         }
     }

Here, it's printing all the items in the array, so it needs to know how big the array is. If you used an std::vector, for instance, you wouldn't need to pass the size as you can just call the size function, but you can't do that with C style arrays, so you need to pass that size as a parameter if you want to write a function that behaves differently depending on the size).

Or here's a more practical example of your code where the size parameter is used to avoid the undefined behavior:

void say(int ar[],int sizeAn)
 {
     cout<<ar[1]<<endl;
     if (sizeAn >= 8){
         cout<<ar[7]<<endl;
     }
 }

Now it's the same as your code with the change that it's only printing the element 7 if it actually exists.

Upvotes: 3

Related Questions