简体   繁体   中英

Using an array in a for loop - C++

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?

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.

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 .

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.

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

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)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM