2

We know it can in Java and JavaScript.

But the question is, can the condition below ever evaluate to true in C or C++?

if(a==1 && a==2 && a==3) 
    printf("SUCCESS");

EDIT

If a was an integer.

Mystical
  • 2,505
  • 2
  • 24
  • 43
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/186211/discussion-on-question-by-eternal-darkness-can-a1-a2-a3-ever-evalu). – Samuel Liew Jan 05 '19 at 04:10

5 Answers5

7

Depends on your definition of "a is an integer":

int a__(){ static int r; return ++r; }
#define a a__()  //a is now an expression of type `int`
int main()
{
    return a==1 && a==2 && a==3; //returns 1
}

Of course:

int f(int b) { return b==1&&b==2&&b==3; }

will always return 0; and optimizers will generally replace the check with exactly that.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
6

If a is of a primitive type (i.e all == and && operators are built in) and you are in defined behavior, and there's no way for another thread to modify a in the middle of execution (this is technically a case of undefined behavior - see comments - but I left it here anyway because it's the example given in the Java question), and there is no preprocessor magic involved (see chosen answer), then I don't believe there is anything way for this to evaluate to true. However, as you can see by that list of conditions, there are many scenarios in which that expression could evaluate to true, depending on the types used and the context of the code.

Nick Mertin
  • 1,149
  • 12
  • 27
  • 5
    If a thread could modify the value in the middle of execution and `a` is a primitive type you would have undefined behavior, so your earlier condition of being in defined behavior already covers that condition. – François Andrieux Jan 04 '19 at 15:28
  • @FrançoisAndrieux I'm going to assume you're right here, I don't know enough about UB to dispute. – Nick Mertin Jan 04 '19 at 15:35
  • @FrançoisAndrieux Does that apply also if a is `volatile int` (and written to by some external hardware)? – Max Langhof Jan 04 '19 at 15:45
  • @MaxLanghof No, but in that case it's not another thread doing the writing and wouldn't be covered by the redundant condition anyway. Edit : In the case of using `volatile` to try to eliminate a race condition with another thread rather than mapped hardware device, see [this answer](https://stackoverflow.com/a/26308498/7359094) on why `volatile` does not prevent race conditions. – François Andrieux Jan 04 '19 at 15:46
  • Modifying `a` in the middle of execution is not necessarily UB. See my answer about `volatile` case. – Eugene Sh. Jan 04 '19 at 15:55
  • @EugeneSh. I don't think examples that depend on extensions are applicable counter-examples for a discussion on portable c++. – François Andrieux Jan 04 '19 at 16:01
  • @FrançoisAndrieux The extension is to make the example real. But it is demonstrating a situation which does not contradict the standard C. `volatile` is standard and it is telling that the value of the object can be modified by the ways unknown to implementation. It does not specify how to make such a variable. – Eugene Sh. Jan 04 '19 at 16:06
  • Volatile does not contribute to resolving race conditions. [1](https://stackoverflow.com/a/4558031/7359094) [2](https://stackoverflow.com/a/26308498/7359094) [3](https://stackoverflow.com/a/2485177/7359094) Any solution where `volatile` works is either a coincidence or non-portable. Edit : External links : [4](https://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming) [5](https://sites.google.com/site/kjellhedstrom2/stay-away-from-volatile-in-threaded-code). – François Andrieux Jan 04 '19 at 16:10
  • @FrançoisAndrieux Who said portable? The OP is asking if there is any situation where the expression can be evaluated to true. This answer is listing one condition - when the value of `a` is changing between the evaluations, and stating that it is UB. I am saying - it is not necessarily UB and showing why. Note, that my answer has nothing to do with threads. – Eugene Sh. Jan 04 '19 at 16:15
  • isocpp.org itself links to [this humorous site](https://isocpp.org/blog/2018/06/is-volatile-useful-with-threads-isvolatileusefulwiththreads.com) to highlight how pervasive the myth that `volatile` is useful for thread safety. – François Andrieux Jan 04 '19 at 16:15
  • @EugeneSh. And your answer is fine for that. But that brings us back to the original issue I raised : *" I don't think examples that depend on extensions are applicable counter-examples for a discussion on portable c++."* This answer makes no mention of specific platforms or extension, so counter-examples that do rely on these are not relevant to it. – François Andrieux Jan 04 '19 at 16:16
  • @FrançoisAndrieux Again, the claim in the parentheses is *incorrect* (and this is my only point here) without any extensions. Once `a` is `volatile` it can change in the middle of execution and it is allowed by C standard and is not undefined. – Eugene Sh. Jan 04 '19 at 16:18
  • @EugeneSh. But it can't be changed by another thread, that would be UB since there is no synchronization between the read and the write. If a specific implementation chooses to provide additional guaranties, it doesn't change that the standard itself doesn't. It would not be reasonable to expect to always have to add "except if the platform happens to provide guaranties for it" every time undefined behavior is mentioned. Accounting for a specific platform's promises isn't talking about the language anymore, it's talking about a particular implementation of the language. – François Andrieux Jan 04 '19 at 16:20
6

If we put macro magic aside, I can see one way that could positively answer this question. But it will require a bit more than just standard C. Let's assume we have an extension allowing to use the __attribute__((at(ADDRESS))); attribute, which is placing a variable at some specific memory location (available in some ARM compilers for example, like ARM GCC). Lets assume we have a hardware counter register at the address ADDRESS, which is incrementing each read. Then we could do something like this:

volatile int a __attribute__((at(ADDRESS)));

The volatile is forcing the compiler to generate the register read each time the comparison is performed, so the counter will increment 3 times. If the initial value of the counter is 1, the statement will return true.

P.S. If you don't like the at attribute, same effect can be achieved using linker script by placing a into specific memory section.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
4

In C, yes it can. If a is uninitialised then (even if there is no UB, as discussed here), its value is indeterminate, reading it gives indeterminate results, and comparing it to other numbers therefore also gives indeterminate results.

As a direct consequence, a could compare true with 1 in one moment, then compare true with 2 instead the next moment. It can't hold both those values simultaneously, but it doesn't matter, because its value is indeterminate.

In practice I'd be surprised to see the behaviour you describe, though, because there's no real reason for the actual storage to change in memory in the time between the two comparisons.


In C++, sort of. The above is still true there, but reading an indeterminate value is always an undefined operation in C++ so really all bets are off.

Optimisations are allowed to aggressively bastardise your code, and when you do undefined things this can quite easily result in all manner of chaos.

So I'd be less surprised to see this result in C++ than in C but, if I did, it would be an observation without purpose or meaning because a program with undefined behaviour should be entirely ignored anyway.


And, naturally, in both languages there are "tricks" you can do, like #define a (x++), though these do not seem to be in the spirit of your question.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • That's kind of the point I was trying to make. I understand that in C, the value is fixed to some undeterminate initial value, and unlike in C++ it has to be self-consistent. – François Andrieux Jan 04 '19 at 15:51
  • 1
    @FrançoisAndrieux The notion of indeterminate and self-consistent are completely at odds with each other, in my mind, unless you can find wording to the contrary. Indeterminate means indeterminate! Either there is a deterministic value or there is not (even quite aside from whether we are permitted to observe it!) – Lightness Races in Orbit Jan 04 '19 at 15:51
  • `its value is indeterminate, reading it gives indeterminate results` - why so? It has some value, and it will be read – qrdl Jan 04 '19 at 15:52
  • 1
    @qrdl No, it has an indeterminate value. It's another English way of saying there is no determinate value. Indeterminate does not mean "arbitrary" or "random" or "left-over", it means _indeterminate_! – Lightness Races in Orbit Jan 04 '19 at 15:52
  • @LightnessRacesinOrbit It has an indeterminate value, but accessing this value is like accessing any other value, so result is well-defined. – qrdl Jan 04 '19 at 15:54
  • @qrdl That seems to me like an absurd statement. If it were true, an indeterminate value would be just like a determinate value, so what's the point in calling it indeterminate. I'm sorry, but I don't think you grok "indeterminate". But I concede my C language-lawyering background is weak so maybe I'm missing some C-specific thing that makes "indeterminate" mean "arbitrary but determinate" instead of, well, "indeterminate".... Perhaps you could help me by defining "indeterminate" in C terms? – Lightness Races in Orbit Jan 04 '19 at 15:57
  • 1
    @LightnessRacesinOrbit Ok, I was wrong, I found what C11 says about it in annex J.2: `The behavior is undefined in the following circumstances: [...] The value of an object with automatic storage duration is used while it is indeterminate`. So accessing indeterminate value is indeed UB. – qrdl Jan 04 '19 at 16:04
  • 1
    There seems there was a [defect report](http://www.open-std.org/Jtc1/sc22/WG14/www/docs/dr_451.htm) about this. It's not clear to me if C developers are complaining about the nature of indeterminate values or asking for the standard to clarify it, but it seems to imply that indeterminate values in C are just like indeterminate values in C++ and aren't required to be self-consistent. Edit : The committee response includes : *"The answer to question 1 is "yes", an uninitialized value under the conditions described can appear to change its value."* which I feel is pretty authoritative. – François Andrieux Jan 04 '19 at 16:04
  • @qrdl It's weird that the C standard has that and then the whole another part about how it's undefined if the uninitialized value could have been declared with the register storage class. – Petr Skocik Jan 04 '19 at 16:08
  • @qrdl Cool - but still I wasn't claiming that :) We can have results that change over time without having UB. – Lightness Races in Orbit Jan 04 '19 at 17:28
3

The following program randomly prints seen: yes or seen: no, depending on whether at some point in the execution of the main thread (a == 0 && a == 1 && a == 2) evaluated to true.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

_Atomic int a = 0;
_Atomic int relse = 0;

void *writer(void *arg)
{
  ++relse;
  while (relse != 2);
  for (int i = 100; i > 0; --i)
    {
      a = 0;
      a = 1;
      a = 2;
    }
  return NULL;
}

int main(void)
{
  int seen = 0;
  pthread_t pt;
  if (pthread_create(&pt, NULL, writer, NULL)) exit(EXIT_FAILURE);
  ++relse;
  while (relse != 2);
  for (int i = 100; i > 0; --i)
    seen |= (a == 0 && a == 1 && a == 2);
  printf("seen: %s\n", seen ? "yes":"no");
  pthread_join(pt, NULL);
  return 0;
}

As far as I am aware, this does not contain undefined behavior at any point, and a is of an integer type, as required by the question. Obviously this is a race condition, and so whether seen: yes or seen: no is printed depends on the platform the program is run on. On Linux, x86_64, gcc 8.2.1 both answers appear regularly. If it doesn't work, try increasing the loop counters.

EOF
  • 6,273
  • 2
  • 26
  • 50
  • I wonder how the behavior relying on the scheduler should be classified.. – Eugene Sh. Jan 04 '19 at 16:28
  • @EugeneSh. *Implementation defined* I assume. Also, I haven't tested this on a single-processor machine, so I expect it's running in parallel rather than concurrently. – EOF Jan 04 '19 at 16:29
  • Yeah, but apparently it is inconsistent even on the same implementation (well, seemingly inconsistent). But it is not undefined, I am pretty sure – Eugene Sh. Jan 04 '19 at 16:30
  • @EugeneSh. Honestly, I started out with much higher loop counters and was shocked at how consistently the `seen: yes` answer appeared. I'm kind of surprised at how easy a race this is to hit. – EOF Jan 04 '19 at 16:33