views:

4628

answers:

9

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.

+8  A: 

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.

Pukku
Code example please? :)
Yuval A
Yea I was still typing :)
Pukku
Very nice solution, worked like a charm. thanks!
Yuval A
The sentinel element technique works even with data with no invalid elements, though in that case you'll need to escape the data when it comes in and unescape it when it comes out. But at that point it's probably time to use a class instead.
Brian
+2  A: 

if it's nullterminated, strlen() would work.

Jimmy
Yes, this is the perfect answer for char arrays as in the question, but would not really work for other data types.
Pukku
+1  A: 

You can't determine the size from charArray alone. That information is not automatically passed to the function.

Of course if it's a null-terminated string you can use strlen(), but you have probably considered that already!

Consider passing a std::vector<char> & parameter, or a pair of pointers, or a pointer plus a size parameter.

finnw
I think Yuval's constrained on the function signature.
Calyth
+2  A: 

This is actually more C than C++, in C++ you'd probably rather use a std::vector. However, in C there's no way to know the size of an array. The compile will allow you to do a sizeof if the array was declared in the current scope, and only if it was explicitly declared with a size (EDIT: and "with a size", I mean that it was either declared with an integer size or initialized at declaration, as opposed to being passed as a parameter, thanks for the downvote).

The common solution in C is to pass a second parameter describing the number of elements in the array.

EDIT:
Sorry, missed the part about not wanting to change the method signature. Then there's no solution except as described by others as well, if there's some data that is not allowed within the array, it can be used as a terminator (0 in C-strings, -1 is also fairly common, but it depends on your actual data-type, assuming the char array is hypothetical)

roe
-1. (a) Arrays declared without sizes infer their sizes from their initialisers and this size can be accessed using sizeof(). (b) Using a function template instead (as per Josh Kelley's answer) *does* allow you to infer the size of an array passed to a function.
j_random_hacker
+1  A: 

In order for a function to know the number of items in an array that has been passed to it, you must do one of two things:

  1. Pass in a size parameter
  2. Put the size information in the array somehow.

You can do the latter in a few ways:

  • Terminate it with a NULL or some other sentinel that won't occur in normal data.
  • store the item count in the first entry if the array holds numbers
  • store a pointer to the last entry if the array contains pointers
AShelly
+8  A: 

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.

Josh Kelley
+2  A: 

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.
}
Konrad Rudolph
it has a slight issue 'cause of the strange C++ and C handling of char. if it is signed, values >127 could result in huge size_t values (wrap-around) when converted to size_t. you can fix it by converting to unsigned char first: static_cast<std::size_t>((unsigned char) charArray[0]);
Johannes Schaub - litb
@litb: Well noticed.
Konrad Rudolph
+1  A: 

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.

John Källén
A: 

You are guarranteed to receive 4 in a 32-bit PC and that's the correct answer. because of the reason explained here and here. The short answer is, you are actually testing the sizeof a pointer rather than an array, because "the array is implicitly converted, or decays, into a pointer. The pointer, alas, doesn't store the array's dimension; it doesn't even tell you that the variable in question is an array."

Now that you are using C++, boost::array is a better choice than raw arrays. Because it's an object, you won't loose the dimention info now.

t.g.