views:

117

answers:

4

In C++ given an array like this:

unsigned char bogus1[] = {
  0x2e, 0x2e, 0x2e, 0x2e
};

Is there a way to introspect bogus1 to find out is is four characters long?

A: 
int bogus1_size = sizeof(bogus1) / sizeof(unsigned char);
oraz
I'd recommend writing that as `sizeof(bogus1) / sizeof(bogus1[0])`; that way there is no chance that you accidentally have a different types between in the definition of `bogus1` and your size computation.
R Samuel Klatchko
+10  A: 

Sure:

#include <iostream>

int main()
{
  unsigned char bogus1[] = {
    0x2e, 0x2e, 0x2e, 0x2e
  };

  std::cout << sizeof(bogus1) << std::endl;

  return 0;
}

emits 4. More generally, sizeof(thearray)/sizeof(thearray[0]) is the number of items in the array. However, this is a compile-time operation and can only be used where the compiler knows about that "number of items" (not, for example, in a function receiving the array as an argument). For more generality, you can use std::vector instead of a bare array.

Alex Martelli
"a function receiving the array as an argument" - careful, that needs to be a *reference* argument. Naming an array type directly in an argument declaration causes decay to pointer type, which causes the sizeof quotient to have undesired, implementation-defined behavior.
Potatoswatter
So, if I do "std::clog << bogus1;" why can't std::clog detect the array is only four characters long and not walk off the end of the array looking for a NULL char?
WilliamKF
WilliamKF: because that's pointless: you should just use std::string instead. The rare cases when you know the length of a string at compiletime are not worth the added complexity that you propose.
Stefan Monov
@WilliamKF, `std::clog << ...` is an example of "a function receiving the array as an argument" (by reference, as potatoswatter mentions -- specifically a const referece in this case) -- the function in question, in this case, is `operator<<(std::ostream-).
Alex Martelli
David Rodríguez - dribeas
@WilliamKF: The problem is that there is no `operator<<` overload that takes an array by reference. The array decays into a pointer to the first element and that element is passed to `operator<<`. The semantics associated to that overload are those of c-strings or null terminated strings. If you really need that functionality you can use a more cumbersome approach: `std::copy( array, array+elements, std::ostream_iterator<char>(std::clog))` where `elements` is the number of elements, constant or `sizeof(array)` for a char array, or `sizeof(array)/sizeof(array[0])` or some metaprogramming trick.
David Rodríguez - dribeas
@David, you're right that there is no such overload in the standard library, but it's trivial to write one -- and it makes absolutely no difference to the current question's title (that overload can't introspect the size of the array it gets by const reference).
Alex Martelli
David Rodríguez - dribeas
David Rodríguez - dribeas
+4  A: 

One thing to be careful about with sizeof is making sure you don't accidentally do the sizeof an array that has decayed into a pointer:

void bad(int a[10])
{
    cout << (sizeof(a) / sizeof(a[0])) << endl;
}

int main()
{
    int a[10];
    cout << (sizeof(a) / sizeof(a[0])) << endl;
    bad(a);
}

On my 64 bit machine, this outputs:

10
2

The size computed inside bad is incorrect because a will have decayed to a pointer. In general, don't trust the sizeof an array that was passed as a function argument.

R Samuel Klatchko
A: 

This gets around the pointer decay problem:

#include <cstddef>

template<typename T, std::size_t N>
std::size_t array_size(T(&)[N]) { return N; }

In that it will not compile unless the array size is known.

Anonymous Coward
Yes, it seems by template usage the std::stringstream could learn of the array length and not run off the end.
WilliamKF
@William: Possibly, but then it would almost certainly ignore any null terminators, which is unexpected when printing strings.
Dennis Zickefoose