I read that in C++17 we can initialize variables in if statements like this
if (int length = 2; length == 2)
//execute something
Instead of
int length = 2;
if (length == 2)
//do something
Even though it is shorter, it affects the code readability (especially for people who don't know this new feature), which I suppose is a bad coding practice for large software development.
Is there any advantage of using this feature other than making the code shorter?
It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write
for(int i = 0; i < ... ; ++i) {
// ...
}
Instead of the variable leaking
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Short lived variables are better for several reasons. But to name a couple:
The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.
If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example
if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.
Is there any advantage of using this feature other than making the code shorter?
You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.
Note that you can already do initialization and branching on the result in pre-C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
This is subject to one's personal opinion, but you can consider an explicit condition more readable:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.
The new form of the if statement has many uses.
Currently, the initializer is either declared before the statement and
leaked into the ambient scope, or an explicit scope is used. With the
new form, such code can be written more compactly, and the improved
scope control makes some erstwhile error-prone constructions a bit
more robust.
Open Standard Proposal for If statement with initializer
So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.
I hope it helps!
In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):
if(auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:
if(auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
An example might be throwing an exception:
if(auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.
It is especially useful for logical events. Consider this example:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Seems a bit rough. Unless you are very familiar with OR, AND with negations, you might need to pause and think about this logic - which is generally poor design. With the if-initialization you can add expressiveness.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
the named variable can be re-used inside the if too. E.g:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
This is great, especially given that the variable is scoped and therefore doesn't pollute the space afterwards.
This is an extension of an existing feature, which aids readability in my experience.
if (auto* ptr = get_something()) {
}
Here we both create the variable ptr and we test against it being non-null. The scope of ptr is limited to where it is valid. It is far, far easier to convince yourself that all use of ptr is valid.
But what if we are talking about something that doesn't convert to bool that way?
if (auto itr = find(bob)) {
}
That doesn't work. But with this new feature we can:
if (auto itr = find(bob); itr != end()) {
}
Add a clause saying "when is this initialization valid".
In essence, this gives us a set of tokens that mean "initialize some expression, and when it is valid, do some code. When it is not valid, discard it."
It has been idiomatic to do the pointer-test trick since C++98. Once you have embraced that, this extension is natural.
this is probably a very noob question but I was what the result of this would be:
int someVariable = 1;
while (callFunction(someVariable));
if (someVariable = 1) {
printf("a1");
} else {
printf("a2");
}
callFunction (int i) {
while (i< 100000000) {
i++;
}
return 0;
}
so when you hit the while loop
while (callFunction(someVariable));
does a thread wait at that loop until it finishes and then to
if(someVariable == 1) {
printf("a1");
} else {
printf("a2");
}
or does it skip and move to the if condition, print "a2" and then after the loop has finished goes through the if condition again?
UPDATE: This isn't ment to be valid c code just psuedo, maybe I didn't word it right, basically what I'm trying to figure out is what the different between a loop like while (callFunction(someVariable)); is vs
while (callFunction(someVariable)){}
i also changed the bolded part in my code i.e ** int someVariable = 1; **, I was doing an endless loop which wasn't my intention.
The code inside a function is executed sequentially, by a single thread. Even if you send an other thread to your function it will execute it sequentually as well.
This is true to 99% of programming languages now days.
UPDATE
basically what I'm trying to figure out is what the different between a loop like while (callFunction(someVariable)); is vs while (callFunction(someVariable)){}
No practical difference. ; delimits an empty statement, { } is a scope without statements. Any compiler can be expected to produce identical code.
Of course, if you want to do something in each iteration of the loop, { } creates a "scope" in which you can create types, typedefs and variables as well as call functions: on reaching the '}' or having an uncaught exception, the local content is cleaned up - with destructors called and any identifiers/symbols use forgotten as the compiler continues....
ORIGINAL ANSWER
This...
callFunction(int i){
while (i< 100000000){
i++;
}
return 1;
}
...just wastes a lot of CPU time, if the compiler's optimiser doesn't remove the loop on the basis that it does no externally-visible work - i.e. that there are no side-effects of the loop on the state of anything other that "i" and that that's irrelevant because the function returns without using i again. If always returns "1", which means the calling code...
while (callFunction(someVariable));
...is equivalent to...
while (1)
;
...which simply loops forever.
Consequently, the rest of the program - after this while loop - is never executed.
It's very hard to guess what you were really trying to do.
To get better at programming yourself - understanding the behaviour of your code - you should probably do one or both of:
insert output statements into your program so you can see how the value of variables is changing as the program executes, and whether it's exiting loops
use a debugger to do the same
Your code contains an endless loop before any output:
while (callFunction(someVariable));
Did you mean for the ; to be there (an empty loop), or did you
mean something else? Not that it matters: callFunction
always returns 1, which is converted into true. (If you
really want the loop to be empty, at least put the ; on
a separate line where it can be seen.)
If you do get beyond the while (because you modify some code
somewhere), the if contains an embedded assignment; it's
basically the same as:
if ( (someVariable = 1) != 0 )
Which is, of course, always true. (Most C++ compilers should
warn about the embedded assignment or the fact that the if
always evaluates to true. Or both.)
If your loop completes (it would be sequentially yes, if you fix it), it will print 'a1', since you're doing an assignment in the if, which will always return 1, which evaluates to 'true' for conditionals.
I have a bit unusual situation - I want to use goto statement to jump into the loop, not to jump out from it.
There are strong reasons to do so - this code must be part of some function which makes some calculations after the first call, returns with request for new data and needs one more call to continue. Function pointers (obvious solution) can't be used because we need interoperability with code which does not support function pointers.
I want to know whether code below is safe, i.e. it will be correctly compiled by all standard-compliant C/C++ compilers (we need both C and C++).
function foo(int not_a_first_call, int *data_to_request, ...other parameters... )
{
if( not_a_first_call )
goto request_handler;
for(i=0; i<n; i++)
{
*data_to_request = i;
return;
request_handler:
...process data...
}
}
I've studied standards, but there isn't much information about such use case. I also wonder whether replacing for by equivalent while will be beneficial from the portability point of view.
Thanks in advance.
UPD: Thanks to all who've commented!
to all commenters :) yes, I understand that I can't jump over initializers of local variables and that I have to save/restore i on each call.
about strong reasons :) This code must implement reverse communication interface. Reverse communication is a coding pattern which tries to avoid using function pointers. Sometimes it have to be used because of legacy code which expects that you will use it.
Unfortunately, r-comm-interface can't be implemented in a nice way. You can't use function pointers and you can't easily split work into several functions.
Seems perfectly legal.
From a draft of the C99 standard http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/n843.htm in the section on the goto statement:
[#3] EXAMPLE 1 It is sometimes convenient to jump into the
middle of a complicated set of statements. The following
outline presents one possible approach to a problem based on
these three assumptions:
1. The general initialization code accesses objects only
visible to the current function.
2. The general initialization code is too large to
warrant duplication.
3. The code to determine the next operation is at the
head of the loop. (To allow it to be reached by
continue statements, for example.)
/* ... */
goto first_time;
for (;;) {
// determine next operation
/* ... */
if (need to reinitialize) {
// reinitialize-only code
/* ... */
first_time:
// general initialization code
/* ... */
continue;
}
// handle other operations
/* ... */
}
Next, we look at the for loop statement:
[#1] Except for the behavior of a continue statement in the |
loop body, the statement
for ( clause-1 ; expr-2 ; expr-3 ) statement
and the sequence of statements
{
clause-1 ;
while ( expr-2 ) {
statement
expr-3 ;
}
}
Putting the two together with your problem tells you that you are jumping past
i=0;
into the middle of a while loop. You will execute
...process data...
and then
i++;
before flow of control jumps to the test in the while/for loop
i<n;
Yes, that's legal.
What you're doing is nowhere near as ugly as e.g. Duff's Device, which also is standard-compliant.
As #Alexandre says, don't use goto to skip over variable declarations with non-trivial constructors.
I'm sure you're not expecting local variables to be preserved across calls, since automatic variable lifetime is so fundamental. If you need some state to be preserved, functors (function objects) would be a good choice (in C++). C++0x lambda syntax makes them even easier to build. In C you'll have no choice but to store state into some state block passed in by pointer by the caller.
First, I need to say that you must reconsider doing this some other way. I've rarely seen someone using goto this days if not for error management.
But if you really want to stick with it, there are a few things you'll need to keep in mind:
Jumping from outside the loop to the middle won't make your code loop. (check the comments below for more info)
Be careful and don't use variables that are set before the label, for instance, referring to *data_to_request. This includes iwhich is set on the for statement and is not initialized when you jump to the label.
Personally, I think in this case I would rather duplicate the code for ...process data... then use goto. And if you pay close attention, you'll notice the return statement inside your for loop, meaning that the code of the label will never get executed unless there's a goto in the code to jump to it.
function foo(int not_a_first_call, int *data_to_request, ...other parameters... )
{
int i = 0;
if( not_a_first_call )
{
...process data...
*data_to_request = i;
return;
}
for (i=0; i<n; i++)
{
*data_to_request = i;
return;
}
}
No, you can't do this. I don't know what this will do exactly, but I do know that as soon as you return, your call stack is unwound and the variable i doesn't exist anymore.
I suggest refactoring. It looks like you're pretty much trying to build an iterator function similar to yield return in C#. Perhaps you could actually write a C++ iterator to do this?
It seems to me that you didn't declare i. From the point of declaration completely depends whether or not this is legal what you are doing, but see below for the initialization
In C you may declare it before the loop or as loop variable. But if it is declared as loop variable its value will not be initialized when you use it, so this is undefined behavior. And if you declare it before the for the assignment of 0 to it will not be performed.
In C++ you can't jump across the constructor of the variable, so you must declare it before the goto.
In both languages you have a more important problem, this is if the value of i is well defined, and if it is initialized if that value makes sense.
Really if there is any way to avoid this, don't do it. Or if this is really, really, performance critical check the assembler if it really does what you want.
If I understand correctly, you're trying to do something on the order of:
The first time foo is called, it needs to request some data from somewhere else, so it sets up that request and immediately returns;
On each subsequent call to foo, it processes the data from the previous request and sets up a new request;
This continues until foo has processed all the data.
I don't understand why you need the for loop at all in this case; you're only iterating through the loop once per call (if I understand the use case here). Unless i has been declared static, you lose its value each time through.
Why not define a type to maintain all the state (such as the current value of i) between function calls, and then define an interface around it to set/query whatever parameters you need:
typedef ... FooState;
void foo(FooState *state, ...)
{
if (FirstCall(state))
{
SetRequest(state, 1);
}
else if (!Done(state))
{
// process data;
SetRequest(state, GetRequest(state) + 1);
}
}
The initialisation part of the for loop will not occur, which makes it somewhat redundant. You need to initialise i before the goto.
int i = 0 ;
if( not_a_first_call )
goto request_handler;
for( ; i<n; i++)
{
*data_to_request = i;
return;
request_handler:
...process data...
}
However, this is really not a good idea!
The code is flawed in any case, the return statment circumvents the loop. As it stands it is equivalent to:
int i = 0 ;
if( not_a_first_call )
\\...process_data...
i++ ;
if( i < n )
{
*data_to_request = i;
}
In the end, if you think you need to do this then your design is flawed, and from the fragment posted your logic also.
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
It seems that
if (x=y) { .... }
instead of
if (x==y) { ... }
is a root of many evils.
Why don't all compilers mark it as error instead of a configurable warning?
I'm interested in finding out cases where the construct if (x=y) is useful.
One useful construct is for example:
char *pBuffer;
if (pBuffer = malloc(100))
{
// Continue to work here
}
As mentioned before, and downvoted several times now, I might add this is not specially good style, but I have seen it often enough to say it's useful. I've also seen this with new, but it makes more pain in my chest.
Another example, and less controversial, might be:
while (pointer = getNextElement(context))
{
// Go for it. Use the pointer to the new segment of data.
}
which implies that the function getNextElement() returns NULL when there is no next element so that the loop is exited.
Most of the time, compilers try very hard to remain backward compatible.
Changing their behavior in this matter to throw errors will break existing legitimate code, and even starting to throw warnings about it will cause problems with automatic systems that keep track of code by automatically compiling it and checking for errors and warnings.
This is an evil we're pretty much stuck with at the moment, but there are ways to circumvent and reduce the dangers of it.
Example:
void *ptr = calloc(1, sizeof(array));
if (NULL = ptr) {
// Some error
}
This causes a compilation error.
Simple answer: An assignment operation, such as x=y, has a value, which is the same as the newly assigned value in x. You can use this directly in a comparison, so instead of
x = y; if (x) ...
you can write
if (x = y) ...
It is less code to write (and read), which is sometimes a good thing, but nowadays most people agree that it should be written in some other way to increase readability. For example, like this:
if ((x = y) != 0) ...
Here is a realistic example. Assume you want to allocate some memory with malloc, and see if it worked. It can be written step by step like this:
p = malloc(4711); if (p != NULL) printf("Ok!");
The comparison to NULL is redundant, so you can rewrite it like this:
p = malloc(4711); if (p) printf("Ok!");
But since the assignment operation has a value, which can be used, you could put the entire assignment in the if condition:
if (p = malloc(4711)) printf("Ok!");
This does the same thing, but it is more concise.
Because it's not illegal (in C or C++ anyway) and sometimes useful...
if ( (x = read(blah)) > 0)
{
// now you know how many bits/bytes/whatever were read
// and can use that info. Esp. if you know, say 30 bytes
// are coming but only got 10
}
Most compilers kick up a real stink if you don't put parenthesis around the assignment anyway, which I like.
About the valid uses of if(i = 0)
The problem is that you're taking the problem upside down. The "if" notation is not about comparing two values like in some other languages.
The C/C++ "if" instruction waits for any expression that will evaluate to either a boolean, or a null/non-null value. This expression can include two values comparison, and/or can be much more complex.
For example, you can have:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
Which proves that, in C/C++, the if expression is not limited to == and =. Anything will do, as long as it can be evaluated as true or false (C++), or zero non-zero (C/C++).
Another C++ valid use:
if(MyObject * pObject = dynamic_cast<MyInterface *>(pInterface))
{
pObject->doSomething();
}
And these are simple uses of the if expression (note that this can be used, too, in the for loop declaration line). More complex uses do exist.
About advanced uses of if(i = 0) in C++ (Quoted from myself)
After discovering a duplicate of this question at In which case is if(a=b) a good idea?, I decided to complete this answer with an additional bonus, that is, variable injection into a scope, which is possible in C++, because if will evaluate its expression, including a variable declaration, instead of limiting itself to compare two operands like it is done in other languages:
So, quoting from myself:
Another use would be to use what is called C++ variable injection. In Java, there is this cool keyword:
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
In C++, you can do it, too. I don't have the exact code in mind (nor the exact Dr. Dobb's Journal's article where I discovered it), but this simple define should be enough for demonstration purposes:
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
This is the same way, mixing injection with an if and for declaration. You can declare a primitive foreach macro (if you want an industrial-strength foreach, use Boost's).
See the following articles for a less naive, more complete and more robust implementation:
FOR_EACH and LOCK
Exception Safety Analysis
Concurrent Access Control & C++
How many errors of this kind really happens?
Rarely. In fact, I have yet to remember one, and I have been a professional for the past 8 years.
I guess it happened, but then, in 8 years, I did produce a sizeable quantity of bugs. It's just that this kind of bugs did not happen enough to have me remember them in frustration.
In C, you'll have more bugs because of buffer overruns, like:
void doSomething(char * p)
{
strcpy(p, "Hello, World! How are you \?\n");
}
void doSomethingElse()
{
char buffer[16];
doSomething(buffer);
}
In fact, Microsoft was burned so hard because of that they added a warning in Visual C++ 2008 deprecating strcpy!
How can you avoid most errors?
The very first "protection" against this error is to "turn around" the expression: As you can't assign a value to a constant, this:
if(0 = p) // ERROR: It should have been if(0 == p). IT WON'T COMPILE!
It won't compile.
But I find this quite a poor solution, because it tries to hide behind a style what should be a general programming practice, that is: Any variable that is not supposed to change should be constant.
For example, instead of:
void doSomething(char * p)
{
if(p == NULL) // POSSIBLE TYPO ERROR
return;
size_t length = strlen(p);
if(length == 0) // POSSIBLE TYPO ERROR
printf("\"%s\" length is %i\n", p, length);
else
printf("the string is empty\n");
}
Trying to "const" as many variables as possible will make you avoid most typo errors, including those not inside "if" expressions:
void doSomething(const char * const p) // CONST ADDED HERE
{
if(p == NULL) // NO TYPO POSSIBLE
return;
const size_t length = strlen(p); // CONST ADDED HERE
if(length == 0) // NO TYPO POSSIBLE
printf("\"%s\" length is %i\n", p, length);
else
printf("the string is empty\n");
}
Of course, it is not always possible (as some variables do need to change), but I found than most of the variables I use are constants (I keep initializing them once, and then, only reading them).
Conclusion
Usually, I see code using the if(0 == p) notation, but without the const-notation.
To me, it's like having a trash can for recyclables, and another for non-recyclable, and then in the end, throw them together in the same container.
So, do not parrot an easy style habit hoping it will make your code a lot better. It won't. Use the language constructs as much as possible, which means, in this case, using both the if(0 == p) notation when available, and using of the const keyword as much as possible.
The 'if(0 = x)' idiom is next to useless because it doesn't help when both sides are variables ('if(x = y)') and most (all?) of the time you should be using constant variables rather than magic numbers.
Two other reasons I never use this idiom, IMHO it makes code less readable and to be honest I find the single '='to be the root of very little evil. If you test your code thouroughly (which we all do, obviously) this sort of syntax error turns up very quickly.
Standard C idiom for iterating:
list_elem* curr;
while ( (curr = next_item(list)) != null ) {
/* ... */
}
Many compilers will detect this and warn you, but only if you set the warning level high enough.
For example:
~> gcc -c -Wall foo.c
foo.c: In function ‘foo’:
foo.c:5: warning: suggest parentheses around assignment used as truth value
Is this really such a common error? I learned about it when I learned C myself, and as a teacher I have occasionally warned my students and told them that it is a common error, but I have rarely seen it in real code, even from beginners. Certainly not more often than other operator mistakes, such as for example writing "&&" instead of "||".
So the reason that compilers don't mark it as an error (except for it being perfectly valid code) is perhaps that it isn't the root of very many evils.
The assignment as conditional is legal C and C++, and any compiler that doesn't permit it isn't a real C or C++ compiler. I would hope that any modern language not designed to be explicitly compatible with C (as C++ was) would consider it an error.
There are cases where this allows concise expressions, such as the idiomatic while (*dest++ = *src++); to copy a string in C, but overall it's not very useful, and I consider it a mistake in language design. It is, in my experience, easy to make this mistake, and hard to spot when the compiler doesn't issue a warning.
I think the C and C++ language designers noticed there is no real use in forbidding it because
Compilers can warn about it if they want anyway
Disallowing it would add special cases to the language, and would remove a possible feature.
There isn't complexity involved in allowing it. C++ just says that an expression implicitly convertible to bool is required. In C, there are useful cases detailed by other answers. In C++, they go one step further and allowed this one in addition:
if(type * t = get_pointer()) {
// ....
}
Which actually limits the scope of t to only the if and its bodies.
It depends on the language. Java flags it as an error as only Boolean expressions can be used inside the if parenthesis (and unless the two variables are Boolean, in which case the assignment is also a Boolean).
In C, it is a quite common idiom for testing pointers returned by malloc or if after a fork we are in the parent or child process:
if ( x = (X*) malloc( sizeof(X) ) {
// 'malloc' worked, pointer != 0
if ( pid = fork() ) {
// Parent process as pid != 0
C/C++ compilers will warn with a high enough warning level if you ask for it, but it cannot be considered an error as the language allows it. Unless, then again, you ask the compiler to treat warnings as errors.
Whenever comparing with constants, some authors suggest using the test constant == variable so that the compiler will detect if the user forgets the second equality sign.
if ( 0 == variable ) {
// The compiler will complaint if you mistakenly
// write =, as you cannot assign to a constant
Anyway, you should try to compile with the highest possible warning settings.
Try viewing
if( life_is_good() )
enjoy_yourself();
as
if( tmp = life_is_good() )
enjoy_yourself();
Part of it has to do with personal style and habits. I am agnostic to reading either if (kConst == x) or if (x == kConst). I don't use the constant on the left because historically I don't make that error and I write code as I would say it or would like to read it. I see this as a personal decision as part of a personal responsibility to being a self-aware, improving engineer. For example, I started analyzing the types of bugs that I was creating and started to re-engineer my habits so as not to make them - similar to constant on the left, just with other things.
That said, compiler warnings, historically, are pretty crappy and even though this problem has been well known for years, I didn't see it in a production compiler until the late 80's. I also found that working on projects that were portable helped clean up my C a great deal, as different compilers and different tastes (ie, warnings) and different subtle semantic differences.
I, personally, consider this the most useful example.
Say that you have a function read() that returns the number of bytes read, and you need to use this in a loop. It's a lot simpler to use
while((count = read(foo)) > 0) {
//Do stuff
}
than to try and get the assignment out of the loop head, which would result in things like
while(1) {
count = read(foo);
if(!(count > 0))
break;
//...
}
or
count = read(foo);
while(count > 0) {
//...
count = read(foo);
}
The first construct feels awkward, and the second repeats code in an unpleasant way.
Unless, of course, I've missed some brilliant idiom for this...
There are a lot of great uses of the assignment operator in a conditional statement, and it'd be a royal pain in the ass to see warnings about each one all the time. What would be nice would be a function in your IDE that let you highlight all the places where assignment has been used instead of an equality check - or - after you write something like this:
if (x = y) {
then that line blinks a couple of times. Enough to let you know that you've done something not exactly standard, but not so much that it's annoying.
if ((k==1) || (k==2)) is a conditional
if ((k=1) || (k=2) ) is BOTH a conditional AND an assignment statement
Here's the explanation
Like most languages, C works inner-most to outermost in order by operator precedence.
First, it tries to set k to 1, and succeeds.
Result: k = 1 and Boolean = 'true'
Next: it sets k to 2, and succeeds.
Result: k = 2 and Boolean = 'true'
Next: it evaluates (true || true)
Result: k still = 2, and Boolean = true
Finally, it then resolves the conditional: If (true)
Result: k = 2 and the program takes the first branch.
In nearly 30 years of programming I have not seen a valid reason for using this construct, though if one exists it probably has to do with a need to deliberately obfuscate your code.
When one of our new people has a problem, this is one of the things I look for, right along with not sticking a terminator on a string, and copying a debug statement from one place to another and not changing the '%i to '%s' to match the new field they are dumping.
This is fairly common in our shop because we constantly switch between C and Oracle PL/SQL; if( k = 1) is the correct syntax in PL/SQL.
It is very common with "low level" loop constructs in C/C++, such as with copies:
void my_strcpy(char *dst, const char *src)
{
while((*dst++ = *src++) != '\0') { // Note the use of extra parentheses, and the explicit compare.
/* DO NOTHING */
}
}
Of course, assignments are very common with for loops:
int i;
for(i = 0; i < 42; ++i) {
printf("%d\n", i);
}
I do believe it is easier to read assignments when they are outside of if statements:
char *newstring = malloc(strlen(src) * sizeof(char));
if(newstring == NULL) {
fprintf(stderr, "Out of memory, d00d! Bailing!\n");
exit(2);
}
// Versus:
if((newstring = malloc(strlen(src) * sizeof(char))) == NULL) // ew...
Make sure the assignment is obvious, thuogh (as with the first two examples). Don't hide it.
As for accidental uses ... that doesn't happen to me much. A common safeguard is to put your variable (lvalues) on the right hand side of the comparison, but that doesn't work well with things like:
if(*src == *dst)
because both oprands to == are lvalues!
As for compilers ... who can blame 'em? Writing compilers is difficult, and you should be writing perfect programs for the compiler anyway (remember GIGO?). Some compilers (the most well-known for sure) provide built-in lint-style checking, but that certainly isn't required. Some browsers don't validate every byte of HTML and Javascript it's thrown, so why would compilers?
There are several tactics to help spot this .. one is ugly, the other is typically a macro. It really depends on how you read your spoken language (left to right, right to left).
For instance:
if ((fp = fopen("foo.txt", "r") == NULL))
Vs:
if (NULL == (fp = fopen(...)))
Sometimes it can be easier to read/write (first) what your testing for, which makes it easier to spot an assignment vs a test. Then bring in most comp.lang.c folks that hate this style with a passion.
So, we bring in assert():
#include <assert.h>
...
fp = fopen("foo.txt", "r");
assert(fp != NULL);
...
when your at the midst, or end of a convoluted set of conditionals, assert() is your friend. In this case, if FP == NULL, an abort() is raised and the line/file of the offending code is conveyed.
So if you oops:
if (i = foo)
insted of
if (i == foo)
followed by
assert (i > foo + 1)
... you'll quickly spot such mistakes.
Hope this helps :)
In short, reversing arguments sometimes helps when debugging .. assert() is your long life friend and can be turned off in compiler flags in production releases.
As pointed out in other answers, there are cases where using assignment within a condition offers a brief-but-readable piece of code that does what you want. Also, a lot of up-to-date compilers will warn you if they see an assignment where they expect a condition. (If you're a fan of the zero-warnings approach to development, you'll have seen these.)
One habit I've developed that keeps me from getting bitten by this (at least in C-ish languages) is that if one of the two values I'm comparing is a constant (or otherwise not a legal lvalue), I put it on the left-hand side of the comparator: if (5 == x) { whatever(); } Then, if I should accidentally type if (5 = x), the code won't compile.
You asked why it was useful, but keep questioning examples people are providing. It's useful because it's concise.
Yes, all the examples which use it can be rewritten - as longer pieces of code.
I have only had this typo once in my 15 years of development. I would not say it is on the top of my list of things to look out for. I also avoid that construct anyway.
Note also that some compilers (the one I use) issue a warning on that code. Warnings can be treated as errors for any compiler worth its salt. They can also be ignored.
Placing the constant on the left side of a comparison is defensive programming. Sure you would never make the silly mistake of forgetting that extra '=', but who knows about the other guy.
The D programming language does flag this as an error. To avoid the problem with wanting to use the value later, it allows declarations sort of like C++ allows with for loops.
if(int i = some_fn())
{
another_fn(i);
}
The compiler won't flag it as an error because it is valid C/C++. But what you can do (at least with Visual C++) is turn up the warning level so that it flags it as a warning and then tell the compiler to treat warnings as errors. This is a good practice anyway so that developers don't ignore warnings.
If you had actually meant = instead of == then you need to be more explicit about it. E.g.,
if ((x = y) != 0)
Theoretically, you're supposed to be able to do this:
if ((x = y))
to override the warning, but that doesn't seem to always work.
In practice I don't do it, but a good tip is to do:
if ( true == $x )
In the case that you leave out an equals, assigning $x to true will obviously return an error.
RegEx sample
RegEx r;
if(((r = new RegEx("\w*)).IsMatch()) {
// ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
// ... do something here
}
Assign a value test
int i = 0;
if((i = 1) == 1) {
// 1 is equal to i that was assigned to a int value 1
}
else {
// ?
}
That's why it's better to write:
0 == CurrentItem
Instead of:
CurrentItem == 0
so that the compiler warns you if you type = instead of ==.