2

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?

WilliamKF
  • 41,123
  • 68
  • 193
  • 295

4 Answers4

10

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
  • 854,459
  • 170
  • 1,222
  • 1,395
  • "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 May 22 '10 at 18:28
  • 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 May 22 '10 at 18:39
  • 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 May 22 '10 at 18:47
  • @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&, const char[]&)`. As I mentioned, it can't "introspect" the size of its argument. Stefan is correct that you should use `std::string` for this kind of things (I mentioned `std::vector` as a more general approach, esp. when the items aren't `char`s;-). – Alex Martelli May 22 '10 at 19:05
  • @Alex, is that so? I believe that the array is decayed into a pointer to the first element and the actual matched signature is: `operator<<( std::ostream&, const char* )` if the function took the array by reference (constant or not) it would have to be templated in the argument array size and there would have to be a different function for dynamically allocated c-strings. – David Rodríguez - dribeas May 23 '10 at 01:52
  • @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(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 May 23 '10 at 01:59
  • @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 May 23 '10 at 03:59
  • @Alex, the overload signature you declared does not take an array by reference, but a *pointer* by reference. If you really want to define a function that takes an array of 3 elements (the size is part of the type) by reference, the signature is `void foo( int (&arg)[3] )`. The syntax `void foo(int array[])` (or `void foo( int array[3])`) are exactly equivalent to `void foo( int *array )` (with the number being discarded by the compiler in the second case), and have nothing to do with arrays. If an array is passed **by reference**, which is not what your code did, you **can** inspect the size. – David Rodríguez - dribeas May 23 '10 at 12:18
  • Consider: `typedef int (array3)[3]; void foo( array3 const & a ) { cout << sizeof(array3) << endl }`, where I have used a typedef to ease the syntax. In this particular case the argument of `foo` is in fact an array of 3 integers, and if you run the code you will printout the correct size. The important point there is: for the compiler to work on a reference which is an alias to an object, it **must** know its size. The compiler cannot possibly alias an object of unknown size (it will allow the function decaration, but not the definition). – David Rodríguez - dribeas May 23 '10 at 12:22
4

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
  • 74,869
  • 16
  • 134
  • 187
0
int bogus1_size = sizeof(bogus1) / sizeof(unsigned char);
Oleg Razgulyaev
  • 5,757
  • 4
  • 28
  • 28
  • 1
    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 May 22 '10 at 18:34
0

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.

liori
  • 40,917
  • 13
  • 78
  • 105