views:

309

answers:

6

I'm trying to simply print out the values contained in an array.

I have an array of strings called 'result'. I don't know exactly how big it is because it was automatically generated.

From what I've read, you can determine the size of an array by doing this:

sizeof(result)/sizeof(result[0])

Is this correct? Because for my program, sizeof(result) = 16 and sizeof(result[0]) = 16 so that code would tell me that my array is of size 1.

However that doesn't appear correct, because if I manually print out the array values like this:

std::cout << result[0] << "\n";
std::cout << result[1] << "\n";
std::cout << result[2] << "\n";
std::cout << result[3] << "\n";
etc...

...then I see the resulting values I'm looking for. The array is upwards of 100+ values in length/size.

It seems like it should be very simple to determine the size/length of an array... so hopefully I'm just missing something here.

I'm a bit of a C++ newb so any help would be appreciated.

+8  A: 

You cannot determine the size of an array dynamically in C++. You must pass the size around as a parameter.

As a side note, using a Standard Library container (e.g., vector) allieviates this.

In your sizeof example, sizeof(result) is asking for the size of a pointer (to presumably a std::string). This is because the actual array type "decays" to a pointer-to-element type when passed to a function (even if the function is declared to take an array type). The sizeof(result[0]) returns the size of the first element in your array, which coincidentally is also 16 bytes. It appears that pointers are 16 bytes (128-bit) on your platform.

Remember that sizeof is always evaluated at compile-time in C++, never at run-time.

+3  A: 

If what you have is a "real" array, then the sizeof(x)/sizeof(x[0]) trick works. If, however, what you have is really a pointer (e.g. something returned from a function) then that trick doesn't work -- you'll end up dividing the size of a pointer by the sizeof a pointer. They are pointers to different types, but on a typical system all pointers are the same size, so you'll get one. Even when the pointers are different sizes, the result still won't have anything to do with how many strings you have.

Jerry Coffin
How are they pointers to different types? Arrays must be homogenous no? What is a "real" array? Does `sizeof` really return the entire array size if it is used in the same scope in which a stack-allocated array is created?
So upon further research, `sizeof(x)` will return the total size of `x`, given that `x` is a stack-allocated array that is defined in the same function. But in this case, you already have a compile-time constant used to declare the size of the array, so you only need know the size of the element-type to compute the size of the array. Once the "array" is passed to a function (even if declared with a formal array parameter), the array is said to "decay" into a pointer. Obviously the compiler could not know the size of an actual array passed at run-time.
You can avoid decay into pointers by using references to arrays. Besides, even if you have access to the array and element size in the current scope, why always use it explicitly and make changes to your code harder?
Georg Fritzsche
@gf: how does that make changes to your code harder (given that you are using a const and not a literal)? When using references to arrays, you MUST supply the array size at compile time. Note, to correct my previous comment, you cannot declare a formal array parameter, only a pointer to the element, or a reference to array type.
@StingRaySc: When you use an unnamed constant you have to replace it everywhere when changing it, same goes for named constants when you change their names. Also, using reference-to-array-parameters for template functions you don't have to supply the array size explicitly.
Georg Fritzsche
When you use *any identifier name whatsoever* you have to replace it everywhere when changing it. I mean, come on! And yes, you don't have to supply the array size explicitly, but somewhere in that same function, **you already have it**!
@STingRaySC:I think gf's point is with something like: `const int size=10, char array[size];`, to change the size, you just change the value of `size`, whereas if you use `10` everywhere, you need to change it everywhere if the size changes (and be sure *not* to change where, for example, the `10` is really intended as a new-line character.
Jerry Coffin
Um, I think that's the first half of his first sentence. The second half, "same goes for named constants when you change their names," implies some sort of distaste with using named constants too. I mean really guys, we're a little beyond programming 101 here -- don't use magic numbers in your code!
+1  A: 

Better use std::vector<std::string> instead of a raw array. Then you don't have to manually manage the arrays memory and you can use the size() method if you want to know the number of elements.

If you use a dynamically allocated raw array you are expected to keep track of its size yourself, the size cannot be obtained from the array. Best save it in an extra variable.

sth
Fair advice, but doesn't answer the question.
Added a paragraph about the size of arrays...
sth
A: 

In String vector use size() method

+3  A: 

As a side comment, there are better ways of checking the size of an array (for the cases where the array is in scope and has not decayed into a pointer) that are typesafe:

// simple: runtime result
template <typename T, std::size_t N>
inline std::size_t sizeof_array( T (&)[N] ) {
   return N;
}

// complex: compile time constant
template <typename T, std::size_t N>
char (&static_sizeof_array( T(&)[N] ))[N];   // declared, not defined
#defined SIZEOF_ARRAY( x ) sizeof(static_sizeof_array(x))

In both cases the compiler will detect if you try to pass in a pointer (dynamic array or decayed array):

void f( int array[] ) { // really: void f( int *array )
{
//   sizeof_array(array);              // compile time error
//   int another[SIZEOF_ARRAY(array)]; // compile time error
}
int main() {
   int array[] = { 1, 2, 3 };
   std::cout << sizeof_array(array) << std::endl; // prints 3
   int another_array[ SIZEOF_ARRAY(array) ];
   std::cout << sizeof_array(another_array) << std::endl; // 3 again
}
David Rodríguez - dribeas
This answer is important. Unlike the C trick, this one will refuse to work on pointers, and you will never end up with 1. Only arrays are accepted. If you have no array to give to the macro, then it means you'll have to keep track of the size of the initial array manually.
Luc Hermitte
Very nice! I didn't know you could do this. I didn't even know about the reference-to-array type until this question. Bjarne makes absolutely no mention of it in the bible.
Could you explain your second template? Not sure what's going on there... Also, it should be pointed out that all of this, clever though it may be, only works when the original stack-allocated array is in scope. The size must already be known by the compiler when it is passed to the template (or to a non-template function that takes a reference-to-array parameter). Of course, in this case, the programmer already has easy enough access to its size! Your first template says `// simple: runtime result`. But this is stricly compile-time...
@STingRaySC: The easy part first: the comment, maybe unfortunate, was intended to say that the value is not a *compile time constant*. Most compilers will inline the value, but you cannot use it for anything that requires a compile time constant (array size, template argument).
David Rodríguez - dribeas
David Rodríguez - dribeas
Very slick indeed. I was hung up on the reference-to-array return value. I presume the significant difference between using this template and a simple call to `sizeof(x)`, where `x` is the array, is that the latter returns the overall size (element size X number of elements), whereas the former returns only the number of elements in the array. Can you now propose a use case where it is preferrable to use either of these templates over a mere use of `sizeof`?
This are both generic solutions to provide the number of elements in the array, not the size it takes in memory. Using `sizeof` to calculate the number of elements (`sizeof(array)/sizeof(array[0])`) is worse than these solutions in that user code can mistakenly pass a pointer (or any other object that has `operator[]` defined taking an integer) and the code will silently compile and provide false results. The advantage of the `sizeof(array)/sizeof(array[0])` over the first variant is that it provides a compile time constant, but that is not an advantage over the second (more complex) variant.
David Rodríguez - dribeas
I can agree that using the `sizeof` approach may be confusing to someone who doesn't understand array decay. But I think even newbies know that the array size is lost when passing to a function, hence the ubiquitous size parameter in any C(-like) function. The paradox is that anyone who doesn't understand this *surely* will not understand how to define and/or use the templates!
I still must say this: All of it is unnecessary. You will *always* know the number of elements of an array allocated on the stack (at compile time). The only case I can think of where anything like this might actually be useful is when a stack-allocated array is initialized, and the size is implicit (you'd have to count the initializers). In this case though, just define a constant to make the array size explicit (good practice anyway, and not a terrible burden to maintain).
I have seen it in different places, and I use it to avoid having to count elements in initializer lists. Note that it has exactly the same use cases as the `sizeof(array)/sizeof(array[0])` in the OP, and if you search in S.O. and other programming forums you will find that there are quite a few questions/answers about array sizes.
David Rodríguez - dribeas
Agreed it has exactly the same use cases as `sizeof(array)/sizeof(array[0])`, which is arguably none (debatable for the counting initializer list case). The OP asked the question because he was unaware of the differences between `T*` and `T[]`, a common source of confusion and a reason that raw arrays and pointers should not be introductory concepts in the language. Nonetheless, your template solutions are quite charming. I may use them myself at some point just for the zen.
I never maintain manually an explicit constant for the size of stack allocated initialized constant arrays. This is not DRY. Instead, I define my array, and use the "second" trick to obtain the exact number of elements. If I need to match this array to another constant (e.g. from an enum), then I use a STATIC_ASSERT (see loki, boost, ...) to check both sizes (the implicit size of the array, and the enum max value) match.
Luc Hermitte
Note: in the same idea, we can have template functions automatically deduce the size of any static array passed: `template <size_t N> void foo(int (i<N;++i) cout << a[i] << " ";}`. Of course this wont work with dynamic, or decayed, arrays.
Luc Hermitte
A: 

Something to be aware of: text can be represented in different methods. An array of text can also be represented in different methods.

Array of pointers to C-Style strings

A common method is to have an array of pointers to char. The issue is that the size of the array doesn't represent the size of all of the text. Also, the ownership of the data or pointer must also be established, as the text may have to be delete (and can the callee delete the text or does the caller?). Because it is an array, the size of the array must always accompany the array in all parameters (unless the array is always a fixed size).

Array of char - packed text

Another method is to pass an array of char and have the strings contiguous in the array. One string follows the termination char of the previous. With this array, the total size of all of the strings is represented, no wasted space. Again, with arrays, the size of the array must accompany the array when passed around.

Array of std::string

In C++, text can be represented using std::string. In this case, the array represents the quantity of strings (similar to the array of C-Strings above). To get the total size of all the strings, one must sum up the size of each individual string. Since this is an array, the size of the array must be passed also.

Summary

During run-time array sizes must accompany the array when the array is passed around. sizeof is only processed at compile time. A simpler structure is std::vector, which handles size and memory allocation dynamically.

Thomas Matthews