I am playing around on edabit.com C++ challenges to refresh myself and I was presented with a challenge where I needed to write a function that would, given a vector, return true if all even indices are even and all odd indices are odd. Otherwise return false. The logic and everything of the function was fine. I know there's a few ways to do this but came up with a (I think) creative solution where instead of running through one for loop with a check if the index is odd or even, I instead wrote two subsequent for loops that increment by two each. One starting at 0 and the other starting at 1. Once it finds a mismatch it returns false. Otherwise at the end it will return true. I am coming from JavaScript where you would not need to redeclare the index variable (if using var not let) but this code failed to compile:
bool isSpecialArray(std::vector<int> arr) {
for(int i = 0; i < arr.size(); i = i + 2){
if(arr[i] % 2 == 1){
return false;
}
}
for(i = 1; i < arr.size(); i = i + 2){
if(arr[i] % 2 == 0){
return false;
}
}
return true;
}
Stating:
error: use of undeclared identifier 'i'
for(i = 1; i < arr.size(); i = i + 2){
^
And of course declaring int in the second for loop cleared the error and the program executed as expected. I'm guessing this is due to garbage collection, but is there a specific rule in the standard that defines this? Or is this maybe a per compiler thing? My understanding of the garbage collection was that the variable once declared is not cleared from memory until is no longer being used in the current function it is defined in. Is this different due to the scope of the for loop, similar to JavaScript's let?
Firstly, JS and C++ have very different models of scopes, and JS is known (sorry) for its bad practices. In C++ the rule is more consistent: the variable is destroyed whenever the program exits the scope. That means that in this code:
for (int i = 0; ; ) {
}
the variable i is defined just for this scope, it cannot be reused in another (not nested) loop, is not seen elsewhere, and the name can be reused or redefined in the inner scopes. This gives you the advantage that it is much easier to control the data flaw and to have less side effects. So the solution for you is to declare another variable i in the second loop:
for(int i = 1; i < arr.size(); i = i + 2){
if(arr[i] % 2 == 0){
return false;
}
}
As long as you are from the JS world I would give you more recommendations. First, do not pass the vector by value (unless you realize what you are doing). Pass it by reference or even better by const reference:
bool isSpecialArray(const std::vector<int> &arr) {
// ...
}
Next, a potentially more efficient and more idiomatic way to increase the index is i += 2.
And the final observation: there is no "creativity" in your solution, but there are drawbacks. This code becomes less cache friendly, and this may be considered as a bad practice.
There is no garbage collection in C++, and even if there were, it wouldn't explain your error. The actual cause is simple: variables declared as part of a for loop go out of scope after the loop.
variable i in your program was declared inside a block or curly braces ({ }). So, variable i is a local variable which can be used only inside that block.
bool isSpecialArray(std::vector<int> arr) {
int i;
for(i = 0; i < arr.size(); i = i + 2){
if(arr[i] % 2 == 1){
return false;
}
}
for(i = 1; i < arr.size(); i = i + 2){
if(arr[i] % 2 == 0){
return false;
}
}
return true;
}
This is the correct way to do it.
c++ does not have garbage collection. Instead, it does deterministic destruction of objects such as i. The way the compiler does this is by cleaning up the object once the scope it's in has ended, and then not allowing you to use that object afterwards.
int main()
{
{
int i;
} // scope of i ends
cout << i; // error, can't access i here
}
for loops have their own scope, so in your example, the i in the first loop is not accessible in the second loop.
Related
I thought to move the return -1 to outside the loop or by instead using an else block, but my apparently this is not possible. Why is this?
Here is the code:
int index_of(vector<string> &names, string name)
{
for (int i = 0; i < names.size(); i++)
{
if (to_lowercase(names[i]) == to_lowercase(name))
{
return i;
}
return -1;
}
}
The function has to guarantee a return value. In your code, your return -1; depends completely on whether or not names.size() is greater than 0. If names.size() is less than or equal to 0, the thread won't even enter the for loop, meaning that your function will return effectively nothing.
The point of this program is to output whether a series of digits (the number of digits undefined) is sorted or not (largest to smallest or smallest to largest).
I have defined my array in my function parameter, and I am trying to use a for loop to store the user's input, as long as it is above 0, in said array.
However, I am getting the error argument of type int is incompatible with parameter of type int*.
The exact error is the argument of type int is incompatible with parameter of type int*.
It is referring to line 22 and 23, these two;
isSorted(list[2000]); and
bool is = isSorted(list[2000]);.
I know this means my for loop is assigning a single value to my variable repeatedly from reading similar questions however I can not figure out how to fix this.
#include <iostream>
using namespace std;
bool isSorted(int list[]);
int main()
{
int i;
int list[2000];
int k = 0;
for (i = 0; i < 2000; i++)
{
int j;
while (j > 0)
{
cin >> j;
list[i] = j;
}
}
isSorted(list[2000]);
bool is = isSorted(list[2000]);
if (is == true)
cout << "sorted";
else
cout << "unsorted";
return 0;
}
bool isSorted(int list[])
{
int i = 0;
for (i = 0; i < 2000; i++)
{
if (list[i] > list[i + 1] || list[i] < list[i - 1])
{
return false;
}
else
return true;
}
}
I removed unused variable k.
Made 2000 parameterized (and set to 5 for testing).
In isSorted you are not allowed to return
true in the else as if your first element test would end in else you would return true immediately not testing other elements. But those later elements can be unsorted as well.
In isSorted you are not allowed to run the loop as for(i = 0; i < 2000; i++), because you add inside the for loop 1 to i and end up querying for i == 1999 list[2000], which is element number 2001 and not inside your array. This is correct instead: for (i = 0; i < 1999; i++). You also do not need to check into both directions.
You cannot call isSorted(list[2000]) as this would call is sorted with an int and not an int array as parameter.
You write int j without initializing it and then query while j > 0 before you cin << j. This is undefined behaviour, while most likely j will be zero, there is no guarantee. But most likely you never enter the while loop and never do cin
I renamed the isSorted as you just check in your example for ascending order. If you want to check for descending order you are welcome to train your programming skills and implementing this yourself.
Here is the code with the fixes:
#include <iostream>
using namespace std;
bool isSortedInAscendingOrder(int list[]);
const int size = 5; // Set this to 2000 again if you want
int main()
{
int i;
int list[size];
for (i = 0; i < size; i++)
{
int j = 0;
while(j <= 0)
{
cin >> j;
if(j <= 0)
cout << "rejected as equal or smaller zero" << endl;
}
list[i] = j;
}
if (isSortedInAscendingOrder(list))
cout << "sorted" << endl;
else
cout << "unsorted" << endl;
return 0;
}
bool isSortedInAscendingOrder(int list[])
{
for (int i = 0; i < size -1; i++)
{
if (list[i] > list[i + 1])
{
return false;
}
}
return true;
}
This is a definition of an array of 2000 integers.
int list[2000];
This is reading the 2000th entry in that array and undefined, because the highest legal index to access is 1999. Remember that the first legal index is 0.
list[2000]
So yes, from point of view of the compiler, the following only gives a single integer on top of being undefined behaviour (i.e. "evil").
isSorted(list[2000]);
You probably should change to this, in order to fix the immediate problem - and get quite close to what you probably want. It names the whole array as parameter. It will decay to a pointer to int (among other things loosing the information of size, but you hardcoded that inside the function; better change that by the way).
isSorted(list);
Delete the ignored first occurence (the one alone on a line), keep the second (the one assigning to a bool variable).
On the other hand, the logic of a your sorting check is flawed, it will often access outside the array, for indexes 0 and 1999. I.e. at the start and end of your loop. You need to loop over slightly less than the whole array and only use one of the two conditions.
I.e. do
for (i = 1; i < 2000; i++)
{
if (list[i] < list[i - 1])
/* ... */
The logic for checking ascending or descending sorting would have to be more complex. The question is not asking to fix that logic, so I stick with fixing the issues according to the original version (which did not mention two-way-sorting).
You actually did not ask about fixing the logic for that. But here is a hint:
Either use two loops, which you can break from as soon as you find a conflict, but do not return from the fuction immediatly.
Or use one loop and keep a flag of whether ascending or descending order has been broken. Then return true if either flag is still clear (or both, in case of all identical values) or return false if both are set.
I'm trying to create a program which allows for a dynamically allocated array to store some integers, increase the maximum size if need be, and then display both the unsorted and sorted array in that order.
Link to my full code is at the bottom.
The first issue I have is the dynamically allocated array going haywire after the size needs to be increase the first time. The relevant code is below.
while (counter <= arraySize)
{
cout <<"Please enter an integer number. Use 999999 (six 9's) to stop\n";
if (counter == arraySize) //If the counter is equal to the size of the array
{ //the array must be resized
arraySize +=2;
int *temp = new int[arraySize];
for (counter = 0; counter < arraySize; counter++)
{
temp[counter] = arrayPtr[counter];
}
delete [] arrayPtr;
arrayPtr = temp;
counter ++; //the counter has to be reset to it's original position
} //which should be +1 of the end of the old array
cin >> arrayPtr[counter];
if (arrayPtr[counter] == sentinel)
{
cout << "Sentinel Value given, data entry ending.\n";
break;
}
counter ++;
}
This produces the unintended operation where instead of waiting for the sentinel value, it just begins to list the integers in memory past that point (because no bounds checking).
The next issue is that my sorting function refuses to run. I tried testing this on 5 values and the program just crashes upon reaching that particular part of code.
The function is called using
sorting (arrayPtr);
but the function itself looks like this:
void sorting (int *arr)
{
int count = 0, countTwo = 0, tempVal;
for (count = 0; arr[count] != 999999; count++) //I figured arr[count] != 999999 is easier and looks better
{ //A bunch of if statements
for (countTwo = 0; arr[countTwo] != 99999; countTwo++)
{
if (arr[countTwo] > arr[countTwo+1])
{
tempVal = arr[countTwo];
arr[countTwo] = arr[countTwo+1];
arr[countTwo+1] = tempVal;
}
}
}
}
Any help on this issue is appreciated.
Link to my source code:
http://www.mediafire.com/file/w08su2hap57fkwo/Lab1_2336.cpp
Due to community feedback, this link will remain active as long as possible.
The link below is to my corrected source code. It is annotated in order to better highlight the mistakes I made and the answers to fixing them.
http://www.mediafire.com/file/1z7hd4w8smnwn29/Lab1_2336_corrected.cpp
The first problem I can spot in your code is in the for loop where counter goes from 0 to arraySize-1, the last two iteration of the loop will access arrrayPtr out of bounds.
Next, at the end of the if (counter == arraySize) there is a counter++; This is not required since at this moment counter is already indexing the array out of bound.
Finally in your sorting function the inner loop looks for the wrong value (99999 instead of 999999), so it never stop and goes out of bounds. To prevent this kind of error, you should define your sentinel as a const in an unnamed namespace and use it through the code instead of typing 999999 (which is error prone...).
This code should perfectly run and give the right result but it doesn't. I used the debugger and everything is ok. c.i and c.j have the right values before the function returns. I make sure there is always a path between start and end so that's not the problem. The path and vis array are filled with the right values too. So the only problem is when it returns. It gives random, big numbers. It works only when i put a return statement at the end of the function. But i can make a function with only one return statement placed in if clause and it works. Is there some kind of rule, so there must be any kind of return statement at the end of a function? To test it, i enter 3x3 two dimensional array with 1's. Any solutions?
Edit: I run it on ideone and it works without return statement at the end of the function. Also a friend of mine tested on his computer it also worked. I use codeblocks on windows 7. What can the problem be?
link to ideone
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <iomanip>
#include <algorithm>
#include <cmath>
using namespace std;
struct crd {
int i,j;
};
bool vis[50][50];
int map[50][50];
int path[50][50];
int di[4] = {1,-1,0,0};
int dj[4] = {0,0,1,-1};
int bfs(crd start, crd end, int n, int m)
{
queue<crd> q;
crd t,c;
q.push(start);
vis[start.i][start.j] = 1;
path[start.i][start.j] = 0;
while (!q.empty()) {
t = q.front();
q.pop();
for (int i=0; i<4; i++) {
c.i = t.i + di[i];
c.j = t.j + dj[i];
if (c.i >= 0 && c.j >= 0 && c.i < n && c.j < m) {
if (map[c.i][c.j] != -1 && vis[c.i][c.j] != 1) {
q.push(c);
vis[c.i][c.j] = 1;
path[c.i][c.j] = path[t.i][t.j] + 1;
if (c.i == end.i && c.j == end.j)
return path[end.i][end.j];
}
}
}
}
// if i put this: return path[end.i][end.j]; it works
}
int main()
{
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++)
cin >> map[i][j];
}
crd s,e;
s.i = s.j = 0;
e.i = e.j = 2;
int sp = bfs(s,e,3,3);
cout << sp << endl;
return 0;
}
The compiler is pretty basic - it has no way of knowing your function will always reach the return inside your if inside the if inside the other if inside the for loop inside the while loop. So it warns you that you might not return anything from the function. The easiest fix would be to return the appropriate value at the end and only break outside the loops at the point you now have your return.
If you declare a function with non-void return type, this functions should return a value. If execution reaches the end of the function, and there is no return <value>; statement there, the result of function would be undefined.
When you "fall off the edge" of a function, the return value is undefined (meaning it could be anything, including values that aren't valid for the type you were returning).
So, every function should have a return at the end. Or some way to say "I didn't expect to get here, stop now" if that's the appropriate thing (say, for example you were looking for a someting in a list, and you don't expect to NOT find it, then having something to print "I couldn't find , expected to. Stopping..." and then exit the program).
I'm not entirely sure what is the RIGHT thing to return if you don't find end.i and end.j, but you certainly should return SOMETHING.
You have two bugs.
It is fine to have no return statement at the end of a function that returns a value, but only if the end of the function can never be reached. In this case, your search algorithm could conceivably fail to find a path (depending of course on the contents of the input data), and then the outer loop will terminate without the return statement ever having been executed. You must have another return statement after the while loop, to account for this possibility. It should not return path[end.i][end.j]; Instead it should return a special value that means the search failed. This is the bug which the compiler has detected and is telling you about.
There is an error somewhere in your breadth-first search logic which is causing paths not to be found even when they do exist. You need to debug that yourself.
i'm using visual studio 2010 and when i do something like
for(int i = 0, j = 0; i < 10; i++)
{
if(m_Var == 1)
j++;
}
if(j == 0)//This line errors undeclared identifier
DoSomething();
I have declared j in the for loop so why is it erroring "undeclared identifier"?
another example would be
for(int i = 0; i < 10; i++)
{
m_Var1++;
}
for(i = 0; i < 200; i++)//This line errors undeclared identifier
{
m_Var2++;
}
that code errors even though it is declared in the for loop, but why? is there a way to do this without having to declare i BEFORE the loop but declare it within the loop instead like in the above examples?
In the first example, j only exists in the scope of the loop
for(int i = 0, j = 0; i < 10; i++)
{
if(m_Var == 1) j++;
} // i, j exist no longer
In the second example, the same applies to i. It's scope is the first loop. You can even check it without a loop:
{
int i = 0;
} // end of scope, end of i
i++; // error,
As I recall, some very early versions of C++ had variables defined in for loops scoped to the block containing the loop. In modern C++, the scope is restricted to the for loop itself. For example:
void foo() {
for (int i = 0; i < N; i ++) {
// i is visible here
}
// In very old C++, i is visible here.
// In modern C++, i is not visible here.
}
Visual Studio actually has an option to enable the old behavior; you can turn off "Force Conformance in For Loop Scope", under Configuration Properties --> C/C++ --> Language. The corresponding compiler command-line option is /Zc:forScope-. But unless you specifically need this to compile old code that you can't afford to fix, I strongly recommend leaving it with the default setting, which conforms to the modern C++ rule. If you're writing new code, just follow the modern C++ rule. If you need a variable to be visible outside the loop, then declare it outside the loop.
Because you declare it in the loop so once the loop has finished, your j variable is out of scope. When you declare variables inside the for statement like this
for(int i = 0, j = 0; i < 10; i++)
the variable only has loop scope.
I have declared j in the for loop ...
Yes, you have. And you can only use it in that for loop, because its scope ends at the closing brace of that loop. You cannot use it after that.
If you want to use it afterwards, you must move the scope out a little bit, but not so far that it will affect other stuff (localisation is still a good thing). One method is to use block scope, something like:
{
int i, j; // Scope starts here,
for (i = 0, j = 0; i < 10; i++)
if (m_Var == 1)
j++;
if (j == 0) // still exists here,
DoSomething();
} // and ends here.
This still limits i and j to a specific, small area, but allows j to "escape" from the if statement.
c++ uses block scope for that variable - msdn docs