Consider this code:
int main()
{
int i;
int ints[3];
ints[0] = 0;
ints[1] = 1;
ints[2] = 2;
for(i = 0; (ints[i])<4 && i<3; i++)
{
printf("%d\n", ints[i]);
}
}
Is there a reason I shouldn't do this sort of conditioning in the loop? I mean, when i becomes 3, will it look for a non-existing value at ints[3]? And if yes, is it ok?, since the loop is going to terminate anyway? It compiles and runs fine, but I was just wondering if there is some sort of a hidden problem with this. Thanks.
In the last iteration of this loop, the expression ints[i]<4 will access ints[3]. Since this access reads past the end of the array ints, the behavior is undefined in C. So yes, this is not good code. It may work most of the time, but the behavior is ultimately undefined.
It will be better to do i < 3 && ints[i] < 4 because the second part of the statement is evaluated only if the first one is true. The way you have it, it will look for ints[3] that does not exist.
It isn't okay to do this, because ints[3] will indeed be accessed. Now, because this is C, you're not going to get an error (unfortunately), but you'll never know for sure what the outcome would be.
Swapping the statements will solve the problem, because the && operator is optimized so that it doesn't evaluate the second condition if the first one is false.
The 'for' keyword expands out the following way:
int i;
for(i = 0; i < 2; i++)
dothing();
becomes
int i;
i = 0; /* first part of (i = 0; ...) */
beginloop:
if (i >= 2) goto endloop; /* second part of (...; i < 2; ...) */
dothing();
i++; /* final part of (...; ...; i++) */
goto beginloop;
endloop:
So, your code would expand out as follows:
i = 0;
beginloop:
if (ints[i] >= 4)
goto endloop;
if (i >= 3)
goto endloop;
printf(...);
i++;
goto beginloop;
endloop:
While this is fine, there is a problem with the ordering of your tests:
(ints[i] < 4) && (i < 3)
You should always check array indexes first to ensure they are valid before using them:
i = 0:
ints[0] < 4 && 0 < 3
i = 1:
ints[1] < 4 && 1 < 3
i = 2:
ints[2] < 4 && 2 < 3
i = 3:
ints[3] < 4 && 3 < 3 /* illegal access to ints[3] */
As a general rule of thumb, always try to order your conditionals in order of cost unless there is a better reason for the ordering, such as priority: In this case, i < 3 is the cheaper of the two tests, plus it is a constraint on the elements of ints you can access, so you checking the index should have a higher priority - you should check it before using it to tests ints[i] for anything.
The way you have written the loop, your code will try to read the non-existing array element ints [3]. That's undefined behaviour; a consequence is that anything can happen. Right now the whole internet is in uproar because of some code in OpenSSL that invoked undefined behaviour.
The loop terminates, that's okay. But you're accessing an area outside of what you're allowed to. It is undefined behaviour. You may get a segmentation anytime. But you're lucky.
Try putting i<3 before. That would work just fine.
for(i = 0; i<3 && (ints[i])<4; i++)
If i becomes 3, the second part is not evaluated. See short circuit evaluation.
Related
My book says for programming using while-loop, we must first initialize with a number, provide the condition mentioning 'while', and then it's to be followed by the statement to partake in the loop until the condition is met as well as to increment value in the loop.
Example :
i = 1;
while(i<=10)
{
s = s + i;
p = p * i;
i++;
}
But, in case of summing of odd numbers program no such incrementing value has been shown.
And, strangely enough(for me), I get correct result w/o the use of i++. I absolutely cannot wrap my head around why that is the case. Is mentioning i++ or i+1 not really a rule within loops?
int s, i, n;
s = 0;
i = 1;
while (i <= n)
{
s = s + i;
i = i + 2;
}
This line is the incrementing value:
i = i + 2;
The first loop increments by 1 with i++. But since you only want the odd numbers, you need to increment by 2.
You can simplify this to:
i += 2;
There is no such rule that we must use i++ in every loop(and for that matter using i as a loop variable).
As #Barmar indicated, you are incrementing i using the line :
i = i + 2;
There are cases where we need to increment by 3, 10, √n, logn, etc.
There are even cases where we need to run a loop backwards hence, we decrement i.
The point is, the value of i must change at some point otherwise we'll end up in an infinite loop.
Is this correct way of assigning an expression to a variable?
int a == ( i<3 );
And I want to use a for loop like this
for(i=0;a; i++)
The assignment operator is =. So the correct way to assign an expression to a variable is
int a = i < 3;
More accurately, this assigns the value of the expression to a variable. It does not assign the expression itself to the variable. This means that the expression is evaluated immediately. When you do
for(i=0;a; i++)
The value of a will never change even though i does.
The idiomatic way of writing for loops is to write the boolean expression inline:
for(i=0;i<3; i++)
If you have some more complicated calculation to determine when a loop should end, then you can write a function for the calculation. For example:
int condition(int i) {
return i < 3;
}
Now you can write the for loop as
for (i = 0; condition(i); i++)
You can use macros like following
#include <stdio.h>
#define a(i) i < 3
int main(void) {
for(int i =0; a(i); i++) {
printf("%d\n",i);
}
return 0;
}
Output
0
1
2
EDIT
As others said macro is not a good idea when the condition is large. In that case you can make a separate function for the complex logic and use that in your for loop condition part. Like following:
int a(i) {
return i < 3; // Your condition. It may be even more complex as per your requirement.
}
Then you can use that function in your for loop in the following way:
for(int i =0; a(i); i++ ){...}
You cannot do that, why would you even want to? Can you give me an example, where this would be useful? Just curious, maybe we find better solution :)
Also, you can read more about loops at http://en.cppreference.com/w/cpp/language/for
I want to initialize an 16-cel-long array with 0, 1, 2 and 3 by blocks of four cels. So here is my first attempt at this:
int main(void) {
int t[16];
int i;
for (i = 0; i<=15; t[i++]=i/4)
{
printf("%d \n", t[i]);
}
return 0;
}
However, here is what I get. I know I can do it differently by just getting the affectation into the for loop, but why does this not work?
EDIT: Please do note that the printf only serves to check what the loop did put in the array.
The initialization works fine; you're just printing the cell before initializing it. Remember that the loop increment is done after each iteration. If you unroll the loop, you have:
i = 0; /* i is now 0 */
print(t[i]); /* prints t[0] */
t[i++] = i/4; /* sets t[0] */
/* i is now 1 */
print(t[i]); /* prints t[1] */
t[i++] = i/4; /* sets t[1] */
/* i is now 2 */
print(t[i]); /* prints t[1] */
/* etc. */
As well as the off-by-one errors with the loop begin/end that have been mentioned in other posts, this code:
t[i++]=i/4
causes undefined behaviour because i is read and written without a sequence point. "Undefined behaviour" means anything can happen: the value could be 3, or 4, or anything else, or the program could crash, etc.
See this thread for more in-depth discussion, and welcome to C..:)
I do not understand what you are trying to accomplish, but please let me show you a similar piece of code, first.
int main(void) {
int t[16];
int i;
//edited the code; providing standard way to do the task
for (i = 0; i<=15; i++)
{
t[i]=i/4;
printf("%d \n", t[i]);
}
return 0;
}
EDIT:
The while loop should be written that way:
int i = 0;
while (i<=15){
t[i] = i%4;
i++;
}
Which means set t[i] equal to i%4 and then increment i.
Since you are a beginner, I've updated the for loop and it now provides a standard way to do your task. It's better to have a simple increment on the third for loop command; do the rest of the job inside the for loop, as described above.
#naltipar: Yeah, I just forgot to initialize the first cel, just like grawity pointed out. Actually, the version I wrote for myself was with i++ but even then, since the third expression is executed after each loop, it sent out the same result. But whatever, it is fixed now.
However, I've got another problem which I'm sure I'm missing on but still can't figure it out:
int i = 0;
while (i<=15)
t[++i] = i%4;
This was first:
for(i = 0; i<=15; t[++i] = i%4);
but it resulted with an infinite loop. So in order to make sure that's not a problem specific to for, I switched to while andthe same thing still happens. That being said, it doesn't occur if i replace ++i by i++. I unrolled the whole loop and everything seems just fine...
I'm a beginner, by the way, in case you were wondering.
A clearer way to write this would be much less error-prone:
for (i = 0; i < 16; ++i)
printf ("%d\n", (t[i] = i % 4));
Personally I'd code something that way, but I'd never recommend it. Moreover, I don't really see much benefit in condensing statements like that, especially in the most important category: execution time. It is perhaps more difficult to optimize, so performance could actually degrade when compared to simply using:
for (i = 0; i < 16; ++i)
{
t[i] = i % 4;
printf ("%d\n", t[i]);
}
Even if it is you reading your own code, you make it difficult for your future self to understand. KISS (Keep It Simple, Stupid), and you'll find code is easier to write now and just as easy to modify later if you need to.
Using the conversion from the for-loop to the while-loop in the example below, can // fixed block be any valid block of code given that you can add any other blocks of code before or after it in the while-loop?
for (int i = 0; i < 10; i++) {
// fixed block
}
int i = 0;
while (i < 10) {
// any block
// fixed block
// any block
}
To show that the answer may not trivially be "yes", I thought of a block of code that may be a counter example:
for (int i = 0; i < 10; i++) {
i = i * 2;
if (1 < 2) {
print(i);
continue;
}
}
The if-statement is there so that in the while-loop you can add a block of code after the continue; statement without getting a possible compiler error/warning. The output of the for-loop is 0, 2, 6 and 14. Adding i++; either before or after the fixed block of code in the while-loop doesn't work, showing that the answer may not be trivial.
Sure they can, just set up a similar exit condition. I don't think the inverse (while loops converted to for loops) is true though.
Also, yes, you're blocks of code will function the same in both cases, if the code is valid (logically and syntactically), then yes it will work.
I wrote the following program to display all prime numbers up to 150. It is not executing at all. what is so wrong with it?
# include <stdio.h>
int main(void)
{
int p[150], i, j;
for (i = 2; i < 150; i++)
p[i] = 0;
i = 2;
while (i < 150){
if (p[i] == 0)
printf("%i ", i);
for (j = 1; i*j <= 150; j++)
p[i*j] = 1;
i++;
}
return 0;
}
You're accessing p[i*j], which is beyond the valid [0-149] range. The condition i*j <= 150 will evaluate true when i*j is equal to 150, which is off-by-one. It should be i*j < 150.
The stdout stream is buffered. You need to flush at the end of your loop. Try adding a fflush(stdout).
Might be of less importance, but if you care about the resulting array (e.g.: wants to use it later), the value of p[2] is erroneously set to 1. However, your program would still print 2, but that's because your loop prints numbers before changing the value of p[i*j]. Concluding, numbers get printed correctly, but the values in the array are not entirely correct.
i*j <= 150 is incorrect, it should be i*j < 150, because the p array has elements from 0 to 149. The program gets stuck in an infinite loop because of this.
EDIT: The rest of this answer was incorrect, so I've removed it.
As a learning exeercise, try adding some printf's to learn what your program does.
Also bear in mind that, as jweyrich says, that printf without \n in it will not output anything until (possibly) the program exits.