How to determine the size of an array of strings in C++?
Asked Answered
P

8

12

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.

Presber answered 16/2, 2010 at 4:51 Comment(4)
I used sizeof(result)/sizeof(result[0]) for primitive types and it used to work, but if you are using strings, why are you still going with array, go for a vector.Mantle
Please show the declaration for result. Without it, it's impossible to tell what your problem is.Calypso
@David Thornley: I think it is safe to assume he is not dealing with a real array containing one element, the only other way he could get 16 for both sizeof calls.Medullary
@HansPassant Let us assume sensibly that the strings are stored in arrays of char (e.g. char result[][16] = { "maximum", "of", "fifteen", "characters", "per", "string" };)... What evidence is there to suggest that the machine is 128-bit? What does 128-bit even mean? Is it the width of the bus? If so, there are 64-bit machines that don't have 64-bit buses and 32-bit machines that do. Is it the width of the native types? If so, even Pentium Pros back in 1995 had a few 128-bit registers...Arvad
M
13

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.

Medullary answered 16/2, 2010 at 4:56 Comment(0)
R
8

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
}
Re answered 16/2, 2010 at 8:54 Comment(13)
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.Acidforming
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.Medullary
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...Medullary
@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).Patchwork
The second template is the declaration of a function named static_sizeof_array taking an array of N elements of type T as unnamed parameter by reference (( T(&)[N])) and returning a reference to an array of N elements of type char (char (&xxxxx)[N]). No definition of the function is needed as it will only be used as argument to the sizeof operator, so the signature suffices. Since sizeof(char) is always one, applying the sizeof operator to the return type of the function will give you a compile time constant N.Patchwork
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?Medullary
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.Patchwork
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!Medullary
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).Medullary
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.Patchwork
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.Medullary
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.Acidforming
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 (&a)[N]) { for (size_t i=0;i<N;++i) cout << a[i] << " ";}. Of course this wont work with dynamic, or decayed, arrays.Acidforming
M
3

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.

Mazurek answered 16/2, 2010 at 4:59 Comment(8)
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?Medullary
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.Medullary
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?Arcadian
@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.Medullary
@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.Arcadian
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!Medullary
@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.Mazurek
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!Medullary
P
1

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.

Porthole answered 16/2, 2010 at 5:2 Comment(1)
Fair advice, but doesn't answer the question.Medullary
P
1

The sizeof(array)/sizeof(element) works for fixed-length-array of fixed-length-arrays (not of pointers). As an array of strings we most often use a (fixed-length-)array of pointers-to-various-(fixed-)length-strings so this trick wouldn't work. sizeof() is used for objects which size is known at compile time. It's not applicable to dynamically allocated data itself.

When an object contains pointers like in the case of an array of strings, sizeof() returns the size of the highest-level (fixed-size) structure. Often it's just the size of a single pointer. It does not include the size of the allocated data pointed to by the pointers. Because that data actually is not part of the main object, it's indeed one or more separate objects (we have aggregation here instead of composition, see http://en.wikipedia.org/wiki/Object_composition).

In C++ using vectors is very convenient for your needs. Other suitable standard containers could be used too. length() and size() methods are synonyms, see http://www.cplusplus.com/reference/string/string/size/)

P.S. Please note that for std::string s object sizeof(s) is a constant independent of the actual (variable) string length returned by s.length(). The actual allocated memory size is returned by s.capacity() and could be greater than length().

Example using vector array:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main()
{
    string s = "01234";
    cout << "s[" << s.length() << "]=\"" << s << "\"" << endl; 
    cout << "sizeof(s)=" << sizeof(s) << " (implementation dependent)" << endl;
    cout << endl;

    s += "56789012345";
    cout << "s[" << s.length() << "]=\"" << s << "\"" << endl; 
    cout << "sizeof(s)=" << sizeof(s) << " (implementation dependent)" << endl;
    cout << endl;

    vector<string>vs={"12","23","345","456","567","67888","7899999999","8","9876543210"};

    cout << "vs[" << vs.size() << "]={";
    size_t sz=0;
    for (size_t index=0; index<vs.size(); index++)
    {
        sz+=vs[index].size();
        if (index>0)
            cout << ",";
        cout << "\"" << vs[index] << "\":" << vs[index].size();
    }
    cout << "}:" << sz << endl;
    cout << "sizeof(vs)=" << sizeof(vs) << " (implementation dependent)" << endl;

    return 0;
}

Result:

s[5]="01234"
sizeof(s)=8 (implementation dependent)

s[16]="0123456789012345"
sizeof(s)=8 (implementation dependent)

vs[9]={"12":2,"23":2,"345":3,"456":3,"567":3,"67888":5,"7899999999":10,"8":1,"9876543210":10}:39
sizeof(vs)=24 (implementation dependent)
Prehensile answered 2/3, 2014 at 12:40 Comment(0)
T
1
template< class T, size_t N >
std::size_t Length(const T(&)[N])
{
    return N;
};

std::cout << Length(another_array) << std::endl;
Therapy answered 4/4, 2016 at 12:51 Comment(0)
I
0

In String vector use size() method

Ingrowing answered 16/2, 2010 at 6:52 Comment(0)
L
0

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.

Litharge answered 16/2, 2010 at 18:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.