Is v[i++] undefined in C? - c

I'm trying to code a function that checks a through a char* a[], and often end up using something like
if(a[i] == 'x'){
i++;
//...
}
Now I would really like to change this to a[i++] instead, but it doesn't seem to work.
Question:
Am I doing something wrong here, or is there a clean alternative to avoid the i++?
Note: Currently 3 of my 13 rows in the function are i++, which makes it look significantly larger than it really is.

if (a[i++] == 'x') by itself is fine in C. It is, however, semantically different: Your original code only increments i if the condition is true, whereas if (a[i++] == 'x') always increments i.

v[i++] is not undefined if v[i] is not undefined and i++ doesn't cause an overflow of signed integer.
if(a[i++] == 'x'){
...
}
if not equivalent to
if(a[i] == 'x'){
i++;
...
}
because the former inclements i every time this is executed,
but the latter inclements i only if a[i] == 'x' is true.
++i is an alternative to avoid i++. Note that the value of ++i differs from i++. (++i)-1 may be useful.

Related

What does (a[i] = b[i]) != 0 mean in a c strcpy implementation?

In a simplified strcpy(a, b) function I saw such implementation:
for (i = 0; (a[i] = b[i]) != 0; i++) ;
I saw the above statement in a c string copy implementation. I assume it is assigning a[i] to b[i], and then check if a[i] is the termination character?
In which order does the above execute? Does it start with the not equal checking first or the assignment? And is it checking on array a or b?
Is it OK to write code in this compact way as this might cause confusion?
A for loop always checks the condition first
"OK" depends on context of course. If you code like this on a team project, someone is likely to get upset with you. If you do it in your own code and you understand it, then it is OK.

Why is this program not behaving as it should?

I am a CS student preparing for a test in Introduction to CS in C language. I am solving some tests from the past and in one of them there was this question:
Look at the code below and state if there any errors. If so, correct them. Also if the program have any input write it down.
The code that came with the question is this:
#include <stdio.h>
#ifdef SIZE
#define MAX_SIZE (2*(SIZE))
#endif
int main() {
int i;
int arr[MAX_SIZE + 1];
for (i = 0; i < sizeof(arr)/sizeof(int);) {
arr[i] = i++;
printf("%d ", arr[i-1]);
}
return 0;
}
So of course I noticed that it wouldn't compile because the preprocessor would just skip the define part and MAX_SIZE would be undeclared variable.
Beside that there shouldn't be any other compiling errors, I did notice the arr[i] = i++ but that should work even if a bit unpredictably (in the test I will call it an error though), so to test my answer I copied it to my IDE and fixed it to my best of knowledge.
(I picked 5 randomly)
#include <stdio.h>
#define SIZE 5
#ifdef SIZE
#define MAX_SIZE (2*(SIZE))
#endif
int main()
{
int i;
int arr[MAX_SIZE + 1];
for (i = 0; i < sizeof(arr)/sizeof(int);)
{
arr[i] = i++;
printf("%d ", arr[i-1]);
}
return 0;
}
to my surprise the function went into an endless loop printing 8 all the time.
After some digging I figured out that for some reason when i is equal to 10 it stops increasing. Some more digging made me realize that it's only happening when SIZE is an odd number and it won't happen if the ++ is prefix (as in ++i).
If the changes mentioned above are applied, the output is as I expected, except that arr[0] doesn't get assigned.
All of that made me conclude that it is a compiler optimization error of some kind, bit I really would like it if someone could explain to me what is really happening here.
Using the value of a variable while also applying a side effect to it in the same statement is undefined behavior.
Therefore this:
arr[i] = i++;
Is an error, and invokes undefined behavior, meaning that the compiler is allowed to do anything. In your case, this happens to cause an endless loop. Using ++i instead of i++ is also an error invoking undefined behavior. The fact that it "seems to work" is just a coincidence.
In general, in order to be able to use the same variable more than once in the same statement, no side effect can be performed in that statement that alters the value of the variable.
From the C11 standard paragraph 6.5 point 2 (page 76):
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.
[...]
This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a[i] = i;
The problem is at this line:
arr[i] = i++;
This invokes undefined behavior. Change it to
arr[i] = i;
i++;
and it will work the way you want.
Let's take this code:
i=0;
arr[i] = i++;
According to how ++ works, it's reasonable that some array element gets the value 0. However, is it a[0] or a[1] that gets the value 0? i++ will return 0, but after it's done i will have the value 1. This is one of the many cases where C has undefined behavior, which basically means that anything might happen. The standard allows the compiler to produce whatever it wants.

Proper way to write 'one but not both' situation?

This question is really arbitrary so I'll try to explain it as best as I can. I'm looping through two strings of unknown size.
bool check(char *str1, char *str2)
{
char special = 'k';
for (int size_t i = 0; ; i++)
{
}
}
I want the terminating condition of the for loop to be the following:
Leave the loop only if either str1[i] == special OR str2[i] == special, but not both.
For this question, ignore the fact that I might segment fault since I know neither the size nor am I checking for 0x00.
I know how to write this but it's always really messy and involves using ternary conditional operators. What is a better way to write it?
You could use (str1[i] == special) != (str2[i] == special), as suggested here.
This works because in c, == can only return one of the int values 0 or 1 (reference).
You want the XOR operator written as ^ use it like you would and && or or ||. It is true only if one but not both arguments are true.
Oops: now see OP said "For this question, ignore the fact that I might segment fault since I know neither the size nor am I checking for 0x00."
So my concern below is moot. Leaving as a reference.
Since code is working with strings, the loop must terminate on 3 conditions:
Leave the loop if either (str1[i] == special) != (str2[i] == special), but not both.
str1[i] == 0.
str2[i] == 0.
Code code be
for (int size_t i = 0;
((str1[i] == special) != (str2[i] == special)) && str1[i] && str2[i]);
i++) {
...
}
Perhaps a simplification could be had.

C pointer sum "not working"

I do not understand what is the point in the else sentence *nombre=(*nombre)++.
Output shows "fernando" and what i thought it was going to show was "ffsoboep" because of the sum. But it seems that *nombre=(*nombre)+1 is different to *nombre=(*nombre)++;
My question is why is that happening? how does "++" operator works in that case. Thanks.
void recursiva (char * nombre)
{
if (*nombre != '\0')
{
recursiva(nombre+1);
if(*nombre > 'A' && *nombre < 'Z')
{
*nombre=*nombre | 32;
}
else
{
*nombre=(*nombre)++;
printf("%c \n",*nombre);
}
}
}
int main()
{
char nombre[]="Fernando";
recursiva(nombre);
printf("%s",nombre);
}
(*nombre)++
doesn't mean the same thing as
*nombre + 1
It means "return the original value of *nombre, with the side effect of increasing *nombre's value by 1". Note that when exactly the value of *nombre increases is rather vague; while it happens after the value of (*nombre)++ is computed, it might happen before, after, or during the assignment to *nombre, with unpredictable results.
If you want to increase the value of *nombre, you don't need to assign the value of (*nombre)++ back to *nombre, and in fact, trying to do so is undefined behavior. As a first approximation, the program is allowed to do anything, up to and including making demons fly out your nose. Just use ++:
(*nombre)++;
or += 1:
*nombre += 1;
I ran your code and actually got "ffsoboep".
If you see that in other compilers you get "fernando", I believe that the following point is not defined in the standard:
*nombre=(*nombre)++;
This is post increment. The value of (x++) is the value of x before the increment (i.e., x == x++ is true).
The question is when does the increment is done.
If the increment is done immediately or after the evaluation of the right side of the = expression, then you first increment the value, and then re-assign the value of the expression (*nombre)++ (which is the value before the increment) to the already incremented *nombre.
If the increment is done, after all the expression is evaluated, then you first assign the (same) value to *nombre, and only then increment.
I think this is not defined in the standard, and therefore - you might see different behaviours. I encountered similar case in the past.

assignment works as a condition

Consider the following Code,
int i;
while(i=0)
printf("Hello");
Now Generally speaking i=0 is an assignment and not a condition for while to check.
But the GCC compiler lets it go with a warning and even evaluates it correctly (does not execute the print statement).
Why? I usually would do with parenthesis for the truth value but my juniors feel that I am wrong and there is no real reason for the parenthesis in this!
EDIT: Zeroing down on the 'actual' doubt, Please consider the following test case
int callme(){
return 0;
}
int main(int argc,char*argv[]){
int c;
while(c = callme()){
printf("Calling...\n");
}
return 0;
}
The expression i = 0 does 2 things:
Has the side effect of storing o in i
Yields the value 0
I usually would do with parenthesis for the truth value but my juniors
feel that i am wrong and there is no real reason for the parenthesis
in this
It's usually a hint to the compiler meaning "I actually want this, I didn't forget a =, shut up".
For your specific case there's no reason to write if (i = 0): you already know what if (0) does. But it's pretty useful when used as:
if ((i = some_function()))
...
i=0 is always an assignment (unless you have it as part of int i = 0; where it is an initialization). But any non-void expression may appear inside the condition of a while loop and if it evaluates to non-zero, the body of the loop will be executed, and if it is zero, the body of the loop will not be executed.
The notation:
while (i = 0)
printf("Hello\n");
is always equivalent to:
i = 0;
There is very little justification for writing the loop at all.
People do write other expressions:
while (c = getchar())
...process EOF or a non-null character...
But that's usually a bug. It is more likely that you should be writing:
while ((c = getchar()) != EOF)
...process a character - possibly null...
or even:
while ((c = getchar()) != EOF && c != '\0')
...process a non-null character...
The first getchar() loop gets a warning from GCC; the latter two do not because of the the explicit test of the value from the assignment.
The people who write a condition like this:
while ((c = getchar()))
really annoy me. It avoids the warning from GCC, but it is not (IMNSHO) a good way of coding.
When you use an assignment operator such as
a=0;
You assign the value to 'a', and still return the number 0.
To test your question, I tried these lines of codes:
int a;
printf("%d", a=0);
and these lines displayed 0.
Then, I tested another set of codes:
int b;
printf("%d", b=15);
Here, the lines displayed 15.
So, if you do:
while(a=0)
{
printf("zero");
}
The (a=0) statement would return false, thus not displaying anything.
But if you do:
while(a=15)
{
printf("fifteen");
}
The "fifteen" will be displayed endlessly, because the statement (a=15) will return a non zero value, or 15, which is not false, not zero, thus it is true. :)
As cnicutar has told above the assignment also yields the value zero.
Some additional info:
It is a common coding mistake for people to omit an extra '=' whereby the comparison becomes an assignment.
An easy way to avoid this is to write the comparison as below, in which case even if a '=' is missed compiler will give an error
while(0 == i)
{
prinf("Hello");
}

Resources