0

I want to set a test condition to determine the size of an array, and then move through each value of the array in a for loop.

Take this array for example

std::string expenses[] = {"housing", "utilities", "household expenses", "transportation", "food", "medical", "insurance", "entertainment", "clothing", "misc"};  

The brackets are empty, and there are 10 elements in there. How would you create a for loop for this without creating a separate variable that holds an int of 10?

4
  • You can determine the actual array's size as long you're in the same scope. As soon expenses is passed elsewhere, it's decayed to a simple pointer and you cannot determine the size of the originating array anymore. Commented Nov 4, 2014 at 1:17
  • so you're saying once i pass expenses to a separate function, i can't determine it's size in a for loop? i would have to declare "expenses" in that same function in order to use it as i want in my question? Commented Nov 4, 2014 at 1:22
  • 1
    If you pass it to a function, you would need to pass the size in as well. Or use a std::vector instead. Commented Nov 4, 2014 at 1:23
  • Exactly that's what I'm saying. If you need to know the exact size of the array in a function you want to iterate through it, you'll need to pass the size along. Commented Nov 4, 2014 at 1:24

6 Answers 6

1

Use the countof macro, declared like this:

#define countof(a) (sizeof(a)/sizeof((a)[0]))

so you can say:

for (i = 0; i < countof(expenses); i++) ...

As everybody's pointing out, you gotta be a good enough hot-shot to know the difference between an array and a pointer. If you pass a pointer to expenses, of course, countof will be bogus.

Sign up to request clarification or add additional context in comments.

4 Comments

@πάντα ῥεῖ: I get your point about passing a pointer, but countof is still really handy. (I hope you're not a macrophobe :)
I'd prefer the plain expression over the macro, to preserve readability though.
Upvoted as it answers the question, but the caveat about losing the ability when passing the array to a function should probably be added to the answer.
@TheDark Note the OP already clarified this in a comment meanwhile. The question was unclear from the beginning.
1

My immediate inclination would be to tell you to use a vector instead of an array. With a vector, you can get the size quite easily, and (better still) avoid getting the size by using a range-based for loop:

std::vector<std::string> expenses {"housing", "utilities", "household expenses", "transportation", "food", "medical", "insurance", "entertainment", "clothing", "misc"};

// print out the strings, one per line:
for (auto const &s : expenses)
    std::cout << s << "\n";

If you really have to use an array instead of a vector, you can use a function template to compute the size:

template <class T, size_t N>
size_t size(T (&array)[N]) { 
    return N;
}

for (int i=0; i<size(expenses); i++)
    std::cout << expenses[i] << '\n';

The primary advantage of this template function over the usual macro ((sizeof(x)/sizeof(x[0]))) is that it's strongly typed--any attempt at passing a pointer instead of an array simply won't compile (and given how easy it is for an array's name to decay to a pointer, that's significant).

If you have C++11 available, you can use the std::begin and std::end from the standard library to accomplish (roughly) the same:

for (auto s = std::begin(expenses); s != std::end(expenses); ++s)
    std::cout << *s;

Note that although std::begin and std::end were added in C++11, you can use code similar to the size template above to create similar templates for a C++98/03 compiler.

template <class T, size_t N>
T *begin(T (&array)[N]) { 
    return array;
}

template <class T, size_t N>
T *end(T (&array)[N]) { 
    return array + N;
}

These can also be used with standard algorithms, so you could do something like this:

std::copy(begin(expenses), end(expenses), 
          std::ostream_iterator<std::string>(std::cout, "\n"));

Again, note that we've avoided dealing directly with the count or creating subscripts into the array, and just deal with iterators into the array, and the items to which those iterators refer.

Comments

1

If you mean to run through each element (within the same scope), then 'The Dark' is correct:

#include <string>
#include <iostream> 

int main()
{
  std::string expenses[] = {"housing", "utilities", "household expenses", "transportation", "food", "medical", "insurance", "entertainment", "clothing", "misc"};
  std::cout<< "NumEls = " << sizeof(expenses) / sizeof(expenses[0]) << std::endl;
}

produces an output of 10, and replacing the cout with a for loop would allow testing of the strings, for example

for (int i=0; i< (sizeof(expenses)/sizeof(expenses[0])); i++)
{
    std::cout<< "The " << i << "th string is : " << expenses[i] << std::endl;
}

Note this will produce "0th", "1th", etc...

* Caveat *

Reflecting the comments given in the question, our answers seem incomplete without mention of the fact that the sizeof(POINTER) won't give you useful information - or at least, not useful for this. As such, if you want instead to use:

myFunction (std::string someArray[])
{
    for( all the strings in someArray )
    {
        std::cout << someArray[i];
    }
}

then you'll find yourself unable to do so.

Instead, you could use:

myFunction (std::string someArray[], int sizeOfArray)
{
    for(int i=0; i<sizeOfArray; i++)
    {
        std::cout<< someArray[i];
    }
}

but this goes exactly against your question (not storing a separate int)

* Enter std::vector *

A simpler solution is to use a std::vector

The use of a vector allows function calls such as myVector.size() and also loops based automatically on the size of the vector, in the case of more recent (C++11) compilers/compiler options.

Vectors can be happily passed into and out of functions, and if you want to change them, references to vectors are also a simple way to do so - referring to your answer:

inputFunction (std::vector<string> &expenses, budget &info)
{
    for (int i=0; i< expenses.size(); i++)
    {
        std::cout<< "Enter your expense for " << expenses[i] << ": ";
        // Operation to store input as needed
    }
}

On a side note, it seems like you want to link the string for the name of the expense to the value of the expense? If so, consider perhaps using a map. In this case, you'd probably want to consider std::map<std::string, float>.

* Using a std::map *

In using a map, you'll probably want an iterator. An example might be like:

void input(const std::vector<std::string> &exp, std::map<std::string, float> &map)
{
  for (int i=0; i<exp.size(); i++)
  {
    float tempFloat;
    std::cout<< "Please enter the amount for " << exp[i] << ": ";
    std::cin >> tempFloat;
    map.emplace(exp[i], tempFloat);
  }
};

and in main(),

std::map<std::string, float> myMap;
  input(myVec, myMap);
  for (std::map<std::string, float>::iterator it=myMap.begin(); it!=myMap.end(); it++)
  {
    std::cout << "myMap values -> " << it->first << " = " << it->second << std::endl;
  }

This will output each pair you have, using an iterator starting at myMap.begin() and ending at the last entry to your map.

emplace(...) constructs a pair, and then adds it to the map. You should take care not to use insert, which requires a different set of parameters, and is not likely to be what you want here.

The outputs are referenced by iterator->first and iterator->second, the first and second values of each map pair. In this case, those are the string and float that are stored in the map.

2 Comments

I see, I'm trying to fully understand how vectors work right now. I've been using arrays strictly up to this point and my textbook goes over vectors briefly for some reason. I've heard of the concept of maps before but i have no clue how it works.
You can ignore the maps if you want, and use a pair of vectors. In this case, it seems like you have a string stored in expenses, and you want to associate it with a float. Maps are good for this, but they're a fair bit more complicated than vectors, which should be your new best friend :)
0

You can use sizeof(expenses) / sizeof (expenses[0]). Note that you don't need the brackets, but I prefer it.

Comments

0

A number of people have mentioned the (sizeof(expenses)/sizeof(expenses[0])) trick, but if you're going to go that route, in C++ there is a slightly better way to do it using a templated function:

/* returns # of items in array, or will error out at compile time if you try to pass it a pointer instead of an array */
template<typename T, int size> unsigned int array_size(T(&)[size]) {return size;}

This is safer, since it will give you a compile-time error if you accidentally pass in a pointer rather than an array. (The sizeof() version would compile and then do something unexpected at runtime, which is undesirable)

2 Comments

Would it work with a decayed array pointer inside a function?
It would fail to compile in that case; which is the best you can hope for, since there is no way in C/C++ to determine the length of an array that a pointer points to (or even to know whether it is actually pointing to an array).
0

Two possibilities. If you want to iterate over it in the same scope that you define it, you can simply use a ranged based for loop:

for(auto& expense : expenses)
{
    std::cout << expense << std::endl;
}

If you want to be able to pass it to a function, you'd have to to some ugly stuff to fit it into a std::array, then you can use the above range loop on it anywhere.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.