I wanted to distinguish between the end of an array an the rest of the elements (in a for loop), but most examples initializes a variable outside the loop, which I think clutters the loop. The shortest example I have achieved is by looking at pointer addresses in a ranged-based for-loop:
for(auto& x : arr){
cout << x;
if(&x != &*end(arr)-1)
cout << ", ";
}
This doesn't need an extra variable, but I am not 100% sure of the implications from using pointers in C++.
A more (or less?) readable example where I initialize a variable in the for-statement, in a way that looks quite intuitive (edit doesn't give portability to fuctions):
for(int i{0}, len{sizeof(arr)/sizeof(*arr)}; i<l; i++){
cout << arr[i];
if(i!=len-1)
cout << ", ";
}
Is there a more readable/better/shorter way to do this without extra includes?
Are there any cons to these approaches?
Why not do the following?
bool not_first_item = false;
for(auto x : arr){
if (not_first_item) {
cout << ", ";
not_first_item = true;
}
cout << x;
}
It will print a comma before each item except the first one. It will get the result you require without the need of using complicated pointers.
If all you have is a pointer to an element in an array, there is no portable way of detecting the position of that element in an array.
Alternatives; best first:
Use a std::vector. That has similar semantics as a plain old array and has the benefit of carrying in the size.
Pass the size of the array as an additional parameter, with size_t type.
Use a magic value to signify the end of an array.
Note that using &x is pointless as x is a value copy. Consider auto& x instead?
This might help
l=sizeof(arr)
for(int i{0}; i<l-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
or
for(int i{0}; i<sizeof(arr)-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
there is no extra condition.
If showing is the main intention
A concise way that I like, without extra branch:
const char* sep = "";
for (const auto& x : arr) {
std::cout << sep << x;
sep = ", ";
}
Yes it uses extra variable.
As a general rule, you should not discard information that you need. When you use a range-based for loop you abstract away the position of an element in the container (and in fact the form of the container). Therefore, it is not the most appropriate tool for the job. You could do this with an index or an iterator because those hold enough information for you to tell whether the element you are iterating over is the last one.
The standard library offers two helpful functions begin and end that either call the member functions begin and end of STL containers, or pointers to the first and past-the-end elements for C-style arrays. Because of the way the end condition is checked, you don't need anything more than a forward iterator.
assert(std::begin(arr) != std::end(arr));
for (auto it = std::begin(arr); it + 1 != std::end(arr); ++it) {
std::cout << *it << ", ";
}
std::cout << *(std::end(arr) - 1) << '\n';
The above code is fine if you know that you're never going to attempt to print an empty container. Otherwise, you'll need an extra if statement to check for that. Note that even if you have a random access iterator and you use the condition it < std::end(arr) - 1 you might reason that it's fine even for empty arrays, but it is undefined behavior and it might lead to some unexpected bugs when optimizations are turned on.
Not 100% sure what I was looking for but I think that the real answer might be to check for the starting item like #ed_heel proposed, but I really only needed to change what I checked for in the range-based for-loop to make it much neater:
for(auto &x : arr)
{
cout << (&x == arr ? "" : ", ") << x;
}
works since arr is an array (a.k.a. pointer in disguise).
Related
So I got this code, I input three numbers. Now I want to display them in a for loop. From smallest to biggest, and in another loop from biggest to smallest. How can I do this?
int main()
{
int num1, num2, num3;
cout << "Enter first num" << endl;
cin >> num1;
cout << "Enter second num" << endl;
cin >> num2;
cout << "Enter third num" << endl;
cin >> num3;
}
I have done this like this but I think it's not a proper way to do it.
for(int i = 0; i <= 0; i++) {
cout << num1;
cout << num2;
cout << num3;
}
for(int i = 0; i <= 0; i++) {
cout << num3;
cout << num2;
cout << num1;
}
Edit:
is this better?
int main()
{
int numbers[3];
cout << "Enter first num" << endl;
cin >> numbers[0];
cout << "Enter second num" << endl;
cin >> numbers[1];
cout << "Enter third num" << endl;
cin >> numbers[2];
for (int i = 0; i <= 2; i++)
{
cout << numbers[i];
}
for (int i = 2; i >= 0; i--)
{
cout << numbers[i];
}
return 0;
}
What you are trying to do is very common in programming languages. I.e. enter data, process it and output it in one form or another.
A loop is what you use if you want to execute a piece of code a number of times... a number more than 1 that is. In your first example you execute the loop once, making the extra lines of code a bit redundant.
You already updated your code, showing you quickly realized how to used arrays. These types of arrays (i.e. int numbers[3];) are often referred to as C-style arrays, as they were inherited from C. In C++ we can now use std::array<int, 3> numbers;, which more adheres to the C++ style of working. (The std:: part of the type name indicates the type is define in the standard library namespace. The type is part of the C++ language standard.)
The problem with these two types is that they have a static(=fixed) size. I.e. the size has to be know at compile time. This can be quite a limitation if you don't know how many items the user wants to enter. However, the C++ standard defines other container types which can hold a variable amount of items. Of these, std::vector is the dynamic (variable) size counterpart of the array: these both store their items sequentially in memory.
So you can for instance use a loop to add (to the back = push_back()) a number of elements selected by the user to the vector.
#include <vector>
#include <iostream>
[...]
std::vector<int> numbers;
std::cout << "How many numbers do you want to enter?\n";
int N;
std::cin >> N;
numbers.reserve(N); // good practice to reserve memory if you know the number of elements
for (int i = 0; i < N; ++i) {
std::cout << "Enter a number: ";
int number;
std::cin >> number;
numbers.push_back(number);
}
[...]
Note that there is no check on the input: e.g. if the user would enter "-1" after the first question, things would break. I will not consider handling user error in my answer.
You can already see some code duplication in here: cout, type definition, cin. You can extract this in a separate function.
#include <string>
[...]
int ReadUserInput(std::string const& message) {
std::cout << message;
int value;
std::cin >> value;
return value;
}
or even better, you make a function template. I.e. a template for a function: the compiler will generate implementations of this function for you, depending on the type T inferred. I also use the std::string_view now, which can view to different types of strings (std::string, char*)
#include <string_view>
[...]
template<typename T>
T ReadUserInput(std::string_view message = "") {
if (!message.empty()) std::cout << message; //only print is there's an actual message
T value;
std::cin >> value;
return value;
}
Next, the C++ library has more to offer, including a number of algorithms that are commonly used in programming. One of these is a generator that repeatedly calls a function, of which the result is used to assign successive elements in a container. The object that points to a specific element in the container is called an iterator. The C++ standard library offers a convenient iterator type that executes a push_back: the std::back_inserter. The previous code can now be reduced to:
int const N = ReadUserInput<int>("How many numbers do you want to enter?\n");
std::vector<int> numbers;
numbers.reserve(N); // good practice to reserve memory if you know the number of elements
std::generate_n(back_inserter(numbers), N, ReadUserInputGenerator<int>("Enter a number: "));
"But wait", you might ask, "what is this ReadUserInputGenerator?". Well, to make the generate_n work, you need to pass a pointer/handle to a generator function, which is then executed for each element. If we'd just call ReadUserInput<int>("Enter a number: "), then the function would already have been evaluated. So we need to add another intermediate function object that makes this generator function. In the passed we would make a class for this
template<typename T>
class ReadUserInputGenerator {
private:
std::string_view message;
public:
ReadUserInputGenerator(std::string_view message = "") : message(message) {}
T operator()() const { return ReadUserInput(message); }
};
... but now we can do using lambda expressions
template<typename T>
auto ReadUserInputGenerator = [](std::string_view message = "") {
return [message]() { return ReadUserInput<T>(message); };
};
(note to more experienced readers: I'm not really sure about passing string_view by value. I based it on this)
SOOOOO, now we finally have our input. Next you wanted to sort it. Again, this is a common operation. There are actually many ways to sort a collection a values and it is a good excersize to implement these yourself... However, like I mentioned previously, as these kind of operations are common in programming, the C++ standard library actually offers an algorithm for sorting: std::sort.
std::sort(begin(numbers), end(numbers));
^ here begin and end refer to iterators pointing to the begin and end (or actually one past the end) of your vector. You could sort only part of your vector this way. However, the most common case is just begin to end, so in C++20 they've introduced the ranges algorithms, and the previous statement can be reduced to
std::ranges::sort(numbers);
... AAAND now its sorted... printing is next. You can print using a loop... but even there you will have a number of choices. And indexed loop:
for (int i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << ' ';
}
An iterator based for loop:
for (auto it = cbegin(numbers); it != cend(numbers); ++it) {
std::cout << *it << ' ';
}
Note: the 'c' before begin and end denote that it is a "const" qualified iterator, i.e. it may not modify the contents of the object it points to.
Or a range based for loop:
for (int number : numbers) {
std::cout << number << ' ';
}
There is also a special convenience iterator type that can push to the cout: std::ostream_iterator. You can copy the vector to this iterator using the algorithm std::copy
std::copy(cbegin(numbers), cend(numbers), std::ostream_iterator<int>(std::cout, " "));
Note, the second argument of the ostream_iterator is the delimiter, i.e. the string appended after each element. Of course there's also a C++20 ranges version.
std::ranges::copy(numbers, std::ostream_iterator<int>(std::cout, " "));
... FINALLY reversing.
One option is just to reverse all elements in the vector and print them out again using one of the above mentioned methods. There's of course an algorithm to do so: std::reverse.
std::reverse(begin(numbers), end(numbers));
However, this operation modifies the contents of the container(vector), which might be costly. If you don't want to to this, you'll have to loop though your vector in reverse order
for (int i = numbers.size() - 1; i >= 0; --i) {
std::cout << numbers[i] << ' ';
}
This looks complex, and it's easy to make errors.
You could instead use the reverse iterators of vector, to traverse through the vector in reverse order: (you need to add an 'r' to the begin/end)
for (auto it = crbegin(numbers); it != crend(numbers); ++it) {
std::cout << *it << ' ';
}
or
std::copy(crbegin(numbers), crend(numbers), std::ostream_iterator<int>(std::cout, " "));
For C++20, there's no range operation to reverse the vector. However, they introduced "views" that are used to observe the values in the vector in a specific way. One such a way is "observe it in reverse order": std::ranges::view::reverse. So in C++20 you will be able to do:
for (int number : numbers | std::views::reverse) {
std::cout << number << ' ';
}
or
std::ranges::copy(numbers | std::views::reverse, std::ostream_iterator<int>(std::cout, " "));
which both don't modify numbers.
The end code could look a little bit something like this (pre C++20 version):
#include <vector>
#include <iostream>
#include <string_view>
#include <iterator>
#include <algorithm>
template<typename T>
T ReadUserInput(std::string_view message = "") {
if (!message.empty()) std::cout << message; //only print is there's an actual message
T value;
std::cin >> value;
return value;
}
template<typename T>
auto ReadUserInputGenerator = [](std::string_view message = "") {
return [message]() { return ReadUserInput<T>(message); };
};
int main() {
int const N = ReadUserInput<int>("How many numbers do you want to enter?\n");
std::vector<int> numbers;
numbers.reserve(N); // good practice to reserve memory if you know the number of elements
std::generate_n(back_inserter(numbers), N, ReadUserInputGenerator<int>("Enter a number: "));
std::sort(begin(numbers), end(numbers));
std::copy(cbegin(numbers), cend(numbers), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
std::copy(crbegin(numbers), crend(numbers), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
Given that the input doesn't seem to have any meaning other than "some numbers", you should use a container, and the obvious choice would be:
std::vector<int> nums;
From c++20, you don't needs loops at all for this problem, because you can use ranges:
#include<ranges>
namespace rs = std::ranges;
namespace rv = std::views;
and now you can read in numbers like this:
rs::copy_n(std::istream_iterator<int>(std::cin), 3,
std::back_inserter(nums));
I'm not sure if you want to use the order that the user inputs the numbers, but if you want the actual smallest to largest, you can do:
rs::sort(nums);
Now to print out the numbers:
rs::copy(nums,
std::ostream_iterator<int>(std::cout, " "));
and in reverse:
rs::copy(nums | rv::reverse,
std::ostream_iterator<int>(std::cout, " "));
Here's a demo.
You don't need a loop, the one you have it's not really a loop in the sense that it only cycles once, you can use a chained ostream:
cout << num1 << " " << num2 << " " << num3 << "\n";
And
cout << num3 << " " << num2 << " " << num1 << "\n";
But if you want print them sorted by value and you can't use some container where you can apply a sorting algorithm, you'll need some conditionals.
EDIT: Is this better?
Storing it in an array makes it easier to deal with the data, for instance, it will allow you to sort it by value using something as simple as <algorithm> library std::sort.
I have the following main program that creates a Stack object, fills it with integers and then pops them. The code files fine, but the pop_back() part does not seem to work, even after pop_back() it prints all values. How is this possible?
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> myVector; //initalization
int value;
//input in a vector using push_back
for (int i = 0; i < 6;i++){
cin >> value;
myVector.push_back(value);
}
cout<<"Initial vector size:" << myVector.size() <<endl;
for (int i = 0; i < 6;i++){
cout << myVector[i];
}
cout << endl;
myVector.pop_back();
cout<<"Vector size after pop back: " << myVector.size() << endl;
cout << endl;
cout << "First element is: " << myVector.front() << endl;
cout << "Last element is : " << myVector.back() << endl;
for (int i = 0; i < 6;i++){
cout << myVector[i];
}
return 0;
}
Everyone has focused on saying this is undefined behavior fix code, but question was why it works.
To understand why it works you must understand how vector works more or less.
Vector allocates some block of memory to store items.
size of this block is defined by capacity, which says how many items can be stored in vector, without allocating new block of memory
capacity is usually bigger then size of the vector
now when you remove items form vector, capacity and allocated block of memory doesn't change. This is an optimization.
when you remove item from back just destructor is called (for int it does nothing) and size of vector is reduced.
your value is not cleared just marked as out of vector size
so when you use operator[] it doesn't check if you exceed its size. It just returns value at specific adders
since pop_back just reduced size you value is still there
Note if you call shrink_to_fit after pop_back there is a great chance it will and with crash or you will not receive same value. Still this is undefined behavior and anything can happen.
Another way to see your code is bad is to use at which checks if index is in valid range.
std::vector::pop_back function works just fine. After you perform a call to it, you try to print all 6 values instead of 5. Therefore, you are accessing invalid memory. In your case, program prints out the value that was removed but in some other case it could print some garbage value. That's why this is UB - Undefined Behavior.
Try the following and you will see that last element is not in the std::vector:
for (int i = 0; i < myVector.size(); i++) {
std::cout << myVector[i];
}
or, even better, use range-based for loop:
for (auto const i : myVector) {
std::cout << i;
}
The problem is in the way you loop through the vector - you are expecting it to have 6 elements even after you have removed the last element. This is undefined behavior.
Using a range based for would be preferred in both cases where you want to output the contents of the vector:
for (auto i:myVector) {
cout << i;
}
I'm attempting to iterate through a list of 6 'Chess' pieces. Each round they move a random amount and if they land on another then they 'kill' it.
The problem is that when the last piece in my vector kills another piece I'm getting a vector 'out of range' error. I'm guessing it's because I'm iterating through a vector whilst also removing items from it, but I'm not increasing the count when I erase a piece so I'm not entirely sure. Any help would be greatly appreciated.
Here is my vector:
vector<Piece*> pieces;
pieces.push_back(&b);
pieces.push_back(&r);
pieces.push_back(&q);
pieces.push_back(&b2);
pieces.push_back(&r2);
pieces.push_back(&q2);
and this is the loop I iterate using:
while (pieces.size() > 1) {
cout << "-------------- Round " << round << " --------------" << endl;
round++;
cout << pieces.size() << " pieces left" << endl;
i = 0;
while (i < pieces.size()) {
pieces.at(i)->move(board.getMaxLength());
j = 0;
while (j < pieces.size()) {
if (pieces.at(i) != pieces.at(j) && col.detectCollision(pieces.at(i), pieces.at(j))) {
cout << pieces.at(i)->getName() << " has slain " << pieces.at(j)->getName() << endl << endl;
pieces.at(i)->setKills(pieces.at(i)->getKills() + 1);
pieces.erase(pieces.begin() + j);
}
else {
j++;
}
}
i++;
}
}
Solution
pieces.erase(pieces.begin() + j);
break;
Your logic needs a little refinement.
The way you coded it, it seems the "turn based" nature of chess has been replaced by a kind of "priority list" -- the pieces closer to the start of the vector are allowed to move first and, thus, get the priority into smashing other pieces.
I don't know if you want this logic to be right or wrong. Anyway, the trouble seems to be due to unconditionally executing the line
i++;
It should not be executed if you remove a piece for the same reason 'j++' isn't executed: you will jump over a piece.
I have a very strong feeling that this line of code:
i++;
is your culprit which is missing either a needed break condition or another conditional check that is missing from your loops. As it pertains to your nested while loops' conditions since they are based on the current size of your vector and are not being updated accordingly.
while (pieces.size() > 1) {
// ...
while (i < pieces.size()) {
// ...
while (j < pieces.size()) {
// ...
}
}
}
This is due to the fact that you are calling this within the inner most nested loop:
pieces.erase(pieces.begin() + j);
You are inside of a nested while loop and if a certain condition is met you are then erasing the object at this index location within your vector while you are still inside of the inner while loop that you never break from or check to see if the index is still valid.
Initially you are entering this while loop with a vector that has 6 entries, and you call erase on it within the nested loop and now your vector has 5 entries.
This can reek havoc on your loops because your index counters i & j were set according to the original length of your vector with a size of 6, but now the vector has been reduced to a size of 5 while you are still within the inner most nested loop that you never break from nor check to see if the indices are valid. On the next iteration these values are now invalidated as you never break out of the loops to reset the indices according to the new size of your vector, nor check to see if they are valid.
Try running this simple program that will demonstrate what I mean by the indices being invalidated within your nested loops.
int main() {
std::vector<std::string> words{ "please", "erase", "me" };
std::cout << "Original Size: " << words.size() << '\n';
for (auto& s : words)
std::cout << s << " ";
std::cout << '\n';
words.erase(words.begin() + 2);
std::cout << "New Size: " << words.size() << '\n';
for (auto& s : words)
std::cout << s << " ";
std::cout << '\n';
return 0;
}
-Output-
Original Size: 3
please erase me
New Size: 2
please erase
You should save locally pieces.at(i) and use this local variable everywhere you use pieces.at(i).
To avoid both elements out of bound and logical problems you can use std::list.
As aside, you should use std::vector<Piece*> only if these are non-owning pointers, otherwise you should use smart-pointers, probably unique_ptr.
I have finished writing a program that included reversing, expanding and shifting arrays using the pointer requirement asked by the professor. Everything compiles but the answer from the expand function does not return what I wish: adding 0s after the old user input array which asks for the size of the array and the numbers you wish to put into the array. I think my problem may lie from the fact that I include a pointer on something that might not have a reference in the program. Below is my code:
// *numPtr refers to my old user input array and int tamaño is the size of the array
void expandArray(int *numPtr, int tamaño) {
int *nuevoArray = new int[tamaño *2];
for (int i = 0; i<tamaño; i++) {
nuevoArray[i] = numPtr[i];
}
for (int i = tamaño; i < (tamaño*2); i++) {
nuevoArray[i] = 0;
}
std::cout << nuevoArray << " ";
}
As I said, my theory of the code not compiling the way I wish is because I use the *nuevoArray and it has no reference in my main code, but then again, I am just a beginner with C++. I was thinking of just doing a vector, but I think I would not follow the pointer requirements placed by the professor.
If you want to print the contents of nuevoarray, just use a for loop like this:
for (int i = 0; i < (tamaño*2); i++) {
std::cout << nuevoArray[i] << " ";
}
std::cout << "\n";
Also, since you are using new[] to create the array, you should not forget to delete[] it!
you can print your array by using
for (int i = 0 ; i < tamano * 2 ; ++i) {
std::cout << nuevoArray[i] << " ";
}
std::cout << std::endl;
or in c++11
for (auto i : nuevoArray) {
std::cout << i << " ";
}
std::cout << std::endl;
PS: The std::endl will return to the start of the new line and flush the cout buffer.
Your code does appear to be allocating a larger array and correctly copying data from numPtr into the new array and also correctly filling the remainder of the new array with zeros.
You don't explicitly say what you expect this function to output, but I'm guessing you expect it to print out the contents of the new array, and that you believe there's a problem because instead of that, you're seeing it print something like "0x7fb46be05d10".
You're not correctly printing the array out. Instead you're printing the memory address of the first element out. If you want to see the contents, then you need to loop over the elements of the array and print each one out individually.
Here's a function showing one way of doing that:
#include <algorithm>
#include <iterator>
void printArray(int *arr, int n) {
std::copy(arr, arr + n, std::ostream_iterator<int>(std::cout, " "));
}
Now you can replace the line std::cout << nuevoArray << " "; in your existing code with printArray(nuevoArray, tamaño*2);
(Also it sounds like whoever is teaching you C++ should take a look at this presentation from the recent C++ conference, CppCon 2015: Stop Teaching C)
Consider the following program. It creates a set of pointer-to-ints, and uses a custom indrect_less comparator that sorts the set by the value of the pointed-to integer. Once this is done, I then change the value of one of the pointed-to integers. Then, it can be seen the order of the set is no longer sorted (I suppose because the set doesn't know something got changed).
(Don't mind the C++0x loops, I'm running on VS2010)
#include <iostream>
#include <set>
using namespace std;
struct indirect_less {
bool operator()(int* l, int* r) const
{
return *l < *r;
}
};
int main()
{
set<int*, indirect_less> myset;
int* a = new int(5);
int* b = new int(6);
int* c = new int(7);
myset.insert(a);
myset.insert(b);
myset.insert(c);
cout << "Set contains: ";
// (outputs: 5 6 7)
for (auto i = myset.begin(), end = myset.end(); i != end; ++i)
{
cout << **i << " ";
}
cout << endl << "Modifying *a" << endl;
*a = 9; // point of interest
cout << "Set contains: ";
// (outputs: 9 6 7 - unsorted order)
for (auto i = myset.begin(), end = myset.end(); i != end; ++i)
{
cout << **i << " ";
}
cout << endl;
cin.get();
return 0;
}
1) Am I right that I am invoking undefined behavior? Is the entire state of myset invalid after the line *a = 9;?
2) Is the only correct way to do this to erase then re-insert a?
3) Is there any way, once *a = 9; has been run, to re-balance the set in to sorted order, with well-defined behavior?
Yes, std::set assumes elements are immutable. It's possible, if dangerous, to reorder it yourself after each change. I wouldn't recommend it, though: use another collection type.
1) Yes, set doesn't allow modification of it's elements.
2) Besides deleting the old value and inserting the new value, you could also replace the old set with a newly constructed one.
3) No
1) I don't know that the behavior is undefined. The additional twist in this example is that the elements of the set are not altered -- each element of the set is a pointer. Were you to print out the (pointer) elements of the set before and after executing the line '*a = 9", I believe you would find that the pointer values are in the same order before and after the assignment. What has changed is the value that one set element points to. This took place outside the auspices of the set, and so the set has no way of maintaining the order you desire.
2) A qualified "yes". This will force use of indirect_less() to order the elements of the set. Again, be aware that you are ordering the elements of the set, pointers, by the value of each dereferenced pointer. However, this strikes me as somewhat risky, for exactly the reason you describe.
From the legend "Set contains:" in the printed output, I presume that this example strives to form a set of integers. However, the set defined, i.e., "set" actually consists of pointers to integers, not the integers themselves. I believe this misalignment between the desired and actual collections is the underlying cause of the problem.
3) See 2).