48

Is it possible to determine the size of an array if it was passed to another function (size isn't passed)? The array is initialized like int array[] = { XXX } ..

I understand that it's not possible to do sizeof since it will return the size of the pointer .. Reason I ask is because I need to run a for loop inside the other function where the array is passed. I tried something like:

for( int i = 0; array[i] != NULL; i++) {
........
}

But I noticed that at the near end of the array, array[i] sometimes contain garbage values like 758433 which is not a value specified in the initialization of the array..

Charles Khunt
  • 2,375
  • 5
  • 25
  • 25
  • Related question which also contains demonstrations of how to do this: [When a function has a specific-size array parameter, why is it replaced with a pointer?](https://stackoverflow.com/questions/1328223/when-a-function-has-a-specific-size-array-parameter-why-is-it-replaced-with-a-p) – Gabriel Staples Sep 24 '20 at 21:15

10 Answers10

64

The other answers overlook one feature of c++. You can pass arrays by reference, and use templates:

template <typename T, int N>
void func(T (&a) [N]) {
    for (int i = 0; i < N; ++i) a[i] = T(); // reset all elements
}

then you can do this:

int x[10];
func(x);

but note, this only works for arrays, not pointers.

However, as other answers have noted, using std::vector is a better choice.

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • 12
    +1 This is a somewhat of a solution, but will create a different func() symbol for each different array size. That is, in different places the passed array has different sizes, the function will be instantiated that many times. This could be helpful though to insert the size of the array in a call to another function that has the real implementation and receives the size, probably flagging it as inline (not that the compiler must follow your rule...) template inline void wrapper( T (&a)[N] ) { return func( a, N ); } -- with func() being the real function. – David Rodríguez - dribeas Jun 09 '09 at 06:17
  • 1
    sure, the goal would be to make the function which operates on it small enough that it is likely to get inlined. I also like your wrapper idea. – Evan Teran Jun 09 '09 at 14:18
  • 1
    @Anakhand: should state explicitely the template parameter N because it cannot be deduced: max_(foo); – jimifiki Oct 11 '19 at 12:14
  • I just added another, longer example, with some comparisons, here: https://stackoverflow.com/a/64054579/4561887 – Gabriel Staples Sep 24 '20 at 21:22
15

If it's within your control, use a STL container such as a vector or deque instead of an array.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
  • 1
    Agreed. If you don't know about vector, now is a great time to learn! It will make your life much easier. – rlbond Jun 09 '09 at 03:53
9

Nope, it's not possible.

One workaround: place a special value at the last value of the array so you can recognize it.

tekBlues
  • 5,745
  • 1
  • 29
  • 32
  • 17
    ... and get ready to debug the cases when someone places that special value in the middle of the array. In other words: don't do it. Like others said: wither use well-defined containers (e.g. STL) or pass the size of the array along with it as other parameter of the function – Rom Jun 09 '09 at 04:28
  • 1
    There is only one useful reason for using special values in the end of the array: variable length parameters in functions. But even in this case specifiying size of input array preferred. – zabulus Jun 24 '11 at 13:25
  • If one consider saving the length inside the array, I would pass this special value (length of array) at the front of the array and I would increment the pointer so that pointer[-1] is always this length value. This concept is used internally by Microsoft Windows BSTR as far as I know (see SysAllocString). If working with array of strings, there is another possible solution. End of array is determined with double NULL character at the end of the array. – bkausbk Jul 22 '13 at 12:11
6

One obvious solution is to use STL. If it's not a possibility, it's better to pass array length explicitly. I'm skeptical about use the sentinel value trick, for this particular case. It works better with arrays of pointers, because NULL is a good value for a sentinel. With array of integers, it's not that easy - you need to have a "magic" sentinel value, which is not good.

Side note: If your array is defined and initalized as

 int array[] = { X, Y, Z };

in the same scope as your loop, then

sizeof(array) will return it's real size in bytes, not the size of the pointer. You can get the array length as

sizeof(array) / sizeof(array[0])

However, in general case, if you get array as a pointer, you can't use this trick.

Igor Krivokon
  • 10,145
  • 1
  • 37
  • 41
4

You could add a terminator to your int array then step through the array manually to discover the size within the method.

#include<iostream>
using namespace std;

int howBigIsBareArray(int arr[]){
    int counter = 0;
    while (arr[counter] != NULL){
        counter++;
    }
    return counter;
}
int main(){
    int a1[6] = {1,2,3,4,5,'\0'};
    cout << "SizeOfMyArray: " << howBigIsBareArray(a1);
}

This program prints:

SizeOfMyArray: 5

This is an O(n) time complexity operation which is bad. You should never be stepping through an array just to discover its size.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Ali
  • 41
  • 1
  • I do agree with @Eric Leschinski, but now a days every coding contest ask you to write the function prototype in this manner only. Since other languages like Java , C# has a way to find the size. I think Ali's answer is perfect. For instance, http://codeyourwayin.topcoder.com/arena They mentioned the prototype for different languages to be same. – siddhusingh Oct 25 '14 at 10:19
  • You just have to ensure that there are no zeroes in the array because the terminator evaluates to the same as zero – Nav Apr 30 '15 at 06:24
  • 1
    -1 for overgeneralization: "You should never be stepping through an array just to discover its size." Never? How do you think strlen works? – Spike0xff Jun 22 '16 at 15:51
  • Or, if you are going to use this place a count at the beginning of the array, instead of the end, that would become O(1), instead of O(N). Logically, it can be compared to keeping the information about a packet in the headers. – Anthony Pace Feb 02 '20 at 01:29
3

If you can't pass the size, you do need a distinguishable sentinel value at the end (and you need to put it there yourself -- as you've found, you can't trust C++ to do it automagically for you!). There's no way to just have the called function magically divine the size, if that's not passed in and there is no explicit, reliable sentinel in use.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
1

Actually Chucks listing of

for( int i = 0; array[i] != NULL; i++) { ........ }

A sizeof before each call is wasteful and is needed to know what you get.

Works great if you put a NULL at the end of the arrays.

Why?? With embedded designs passing a sizeof in each routine makes each call very large compared to a NULL with each array. I have a 2K PIC16F684 chip and it takes upto 10 percent of the chip with 12 calls using a passed sizeof along with the array. With just the array and Chucks code with NULLS om each array... I get 4 percent needed.

A true case in point.. thanks chuck good call.

  • 2
    sizeof by itself wastes neither time nor space, it is evaluated at compile time. Passing it as an additional parameter does make a function call larger by one or two instructions, which, yes, does matter when you have only 2K of memory. You realize that's... atypical? – Spike0xff Jun 22 '16 at 15:59
1

I originally had this as an answer to this other question: When a function has a specific-size array parameter, why is it replaced with a pointer?, but just moved it here instead since it more-directly answers this question.


Building off of @Richard Corden's answer and @sbi's answer, here's a larger example demonstrating the principles of:

  1. Enforcing a given function parameter input array size using a reference to an array of a given size, like this:

     void foo2(uint8_t (&array)[100]) 
     {
         printf("sizeof(array) = %lu\n", sizeof(array)); 
     }
    

    and:

  2. Allowing a function parameter input array of any size, by using a function template with a reference to an input array of a given template parameter size N, like this:

     template<size_t N>
     void foo3(uint8_t (&array)[N])
     {
         printf("sizeof(array) = %lu\n", sizeof(array)); 
     }
    

Looking at the full example below:

Notice how this function prototype doesn't know the array size at all! (the 100 here is simply a visual hint/reminder to the human user, but has no bearing or influence on the compiler whatsoever!):

void foo(uint8_t array[100]) {}

...this function prototype allows only input arrays of a fixed size of 100:

void foo2(uint8_t (&array)[100]) {}

...and this function template prototype allows arrays of ANY input size AND knows their size statically at compile-time (as that is how templates work):

template<size_t N>
void foo3(uint8_t (&array)[N]) {}

Here's the full example:

You can run it yourself here: https://onlinegdb.com/rkyL_tcBv.

#include <cstdint>
#include <cstdio>

void foo(uint8_t array[100]) 
{
    // is ALWAYS sizeof(uint8_t*), which is 8!
    printf("sizeof(array) = %lu\n", sizeof(array)); 
}

void foo2(uint8_t (&array)[100]) 
{
    printf("sizeof(array) = %lu\n", sizeof(array)); 
}

template<size_t N>
void foo3(uint8_t (&array)[N])
{
    printf("sizeof(array) = %lu\n", sizeof(array)); 
}


int main()
{
    printf("Hello World\n");
    printf("\n");
    
    uint8_t a1[10];
    uint8_t a2[11];
    uint8_t a3[12];
    
    // Is `sizeof(array) = 8` for all of these!
    foo(a1);
    foo(a2);
    foo(a3);
    printf("\n");
    
    // Fails to compile for these 3! Sample error:
    // >     main.cpp:49:12: error: invalid initialization of reference of type ‘uint8_t (&)[100] 
    // >     {aka unsigned char (&)[100]}’ from expression of type ‘uint8_t [10] {aka unsigned char [10]}’
    // >          foo2(a1);
    // >                 ^
    // foo2(a1);
    // foo2(a2);
    // foo2(a3);
    // ------------------
    // Works just fine for this one since the array `a4` has the right length!
    // Is `sizeof(array) = 100`
    uint8_t a4[100];
    foo2(a4);
    printf("\n");

    foo3(a1);
    foo3(a2);
    foo3(a3);
    foo3(a4);
    printf("\n");

    return 0;
}

Sample output:

(compiler warnings, referring to the sizeof call inside foo()):

main.cpp:26:49: warning: ‘sizeof’ on array function parameter ‘array’ will return size of ‘uint8_t* {aka unsigned char*}’ [-Wsizeof-array-argument]                               
main.cpp:23:27: note: declared here                                                                                                                                               

(stdout "standard output"):

Hello World                                                                                                                                                                       
                                                                                                                                                                                  
sizeof(array) = 8                                                                                                                                                                 
sizeof(array) = 8                                                                                                                                                                 
sizeof(array) = 8                                                                                                                                                                 
                                                                                                                                                                                  
sizeof(array) = 100                                                                                                                                                               
                                                                                                                                                                                  
sizeof(array) = 10                                                                                                                                                                
sizeof(array) = 11                                                                                                                                                                
sizeof(array) = 12                                                                                                                                                                
sizeof(array) = 100   
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
1

Can you try appending a null character \0 to the array and then send it? That way, you can just check for \0 in the loop.

Alan Haggai Alavi
  • 72,802
  • 19
  • 102
  • 127
0

Shouldn't this work? for things like Arduino(AVR) c++ at least.

//rename func foo to foo_ then
#define foo(A) foo_(A, sizeof(A))

void foo_(char a[],int array_size){
...
}