Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an array as a function parameter in C++

Tags:

In C++, arrays cannot be passed simply as parameters. Meaning if I create a function like so:

void doSomething(char charArray[])
{
    // if I want the array size
    int size = sizeof(charArray);
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer)
}

I have no way of knowing how big the array is, since I have only a pointer to the array.

Which way do I have, without changing the method signature, to get the size of the array and iterate over it's data?


EDIT: just an addition regarding the solution. If the char array, specifically, was initialized like so:

char charArray[] = "i am a string";

then the \0 is already appended to the end of the array. In this case the answer (marked as accepted) works out of the box, so to speak.

like image 697
Yuval Adam Avatar asked Jan 14 '09 21:01

Yuval Adam


People also ask

Can we pass an array in function as a parameter in C?

An array can be passed to functions in C using pointers by passing reference to the base address of the array and similarly, a multidimensional array can also be passed to functions in C.

Can we send an array as a parameter to a function?

A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name.

Can you pass an array through a function?

C++ does not allow to pass an entire array as an argument to a function. However, You can pass a pointer to an array by specifying the array's name without an index.

When you pass an array to a function the function receives?

The address of the first variable is passed when you pass an array to a function in C. An array passed to a function decays into a pointer to its first element.


4 Answers

Use templates. This technically doesn't fit your criteria, because it changes the signature, but calling code does not need to be modified.

void doSomething(char charArray[], size_t size)
{
   // do stuff here
}

template<size_t N>
inline void doSomething(char (&charArray)[N])
{
    doSomething(charArray, N);
}

This technique is used by Microsoft's Secure CRT functions and by STLSoft's array_proxy class template.

like image 170
Josh Kelley Avatar answered Sep 29 '22 15:09

Josh Kelley


Without changing the signature? Append a sentinel element. For char arrays specifically, it could be the null-terminating '\0' which is used for standard C strings.

void doSomething(char charArray[])
{
    char* p = charArray;
    for (; *p != '\0'; ++p)
    {
         // if '\0' happens to be valid data for your app, 
         // then you can (maybe) use some other value as
         // sentinel
    }
    int arraySize = p - charArray;

    // now we know the array size, so we can do some thing
}

Of course, then your array itself cannot contain the sentinel element as content. For other kinds of (i.e., non-char) arrays, it could be any value which is not legal data. If no such value exists, then this method does not work.

Moreover, this requires co-operation on the caller side. You really have to make sure that the caller reserves an array of arraySize + 1 elements, and always sets the sentinel element.

However, if you really cannot change the signature, your options are rather limited.

like image 24
Reunanen Avatar answered Sep 26 '22 15:09

Reunanen


In general when working with C or low-level C++, you might consider retraining your brain to never consider writing array parameters to a function, because the C compiler will always treat them as pointers anyway. In essence, by typing those square brackets you are fooling yourself in thinking that a real array is being passed, complete with size information. In reality, in C you can only pass pointers. The function

void foo(char a[])
{
    // Do something...
}

is, from the point of view of the C compiler, exactly equivalent to:

void foo(char * a)
{
    // Do something
}

and obviously that nekkid char pointer contains no length information.

If you're stuck in a corner and can't change the function signature, consider using a length prefix as suggested above. A non-portable but compatible hack is to specify the array length in an size_t field located before the array, something like this:

void foo(char * a)
{
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
    int c_len = ((size_t *)a)[-1];
}

Obviously your caller needs to create the arrays in the appropriate way before passing them to foo.

Needless to say this is a horrible hack, but this trick can get out of trouble in a pinch.

like image 6
John Källén Avatar answered Sep 26 '22 15:09

John Källén


It actually used to be a quite common solution to pass the length in the first element of the array. This kind of structure is often called BSTR (for “BASIC string”), even though this also denoted different (but similar) types.

The advantage over the accepted solution is that determining the length using a sentinel is slow for large strings. The disadvantage is obviously that this is a rather low-level hack that respects neither types nor structure.

In the form given below it also only works for strings of length <= 255. However, this can easily be expanded by storing the length in more than one byte.

void doSomething(char* charArray)
{
    // Cast unnecessary but I prefer explicit type conversions.
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0]));
    // … do something.
}
like image 6
Konrad Rudolph Avatar answered Sep 27 '22 15:09

Konrad Rudolph