-1

I have the following program

void swap(float * x, float * y)
{
  float aux
  aux = *x;
  *x = *y;
  *y = aux;
}

int main(void)
{
  double a = 3.5, b = 5.6;
  swap(&a, &b);
  printf("%g %g\n", a, b);
  return 0;
}

The program compiles, it obviously throws some warnings, but it runs, and the values of a, b don't get swapped. I don't understand what is happening, I would've thought either this wouldn't have compiled, or it would work, but this is pretty different.

What is happening?

YoTengoUnLCD
  • 600
  • 7
  • 15
  • `swap(&a, &b);` - `a` and `b` are doubles! – Ed Heal Feb 04 '16 at 01:35
  • Yes, I know, that's what I'm asking, why does the function execute correctly but doesn't do anything? Shouldn't it crash or something? – YoTengoUnLCD Feb 04 '16 at 01:36
  • Perhaps heed the warnings - they are there for a reason. (e.g. mind your head) – Ed Heal Feb 04 '16 at 01:37
  • 4
    It's undefined behavior. Anything can happen. – Barmar Feb 04 '16 at 01:38
  • As an exercise, add this somewhere in your code: `printf("double:%d bytes, float: %d bytes\n", sizeof(double), sizeof(float));` – jamieguinan Feb 04 '16 at 01:39
  • to get to the bottom of this you need to look at how double and floats are stored in memory and see what happens if they are mixed up. – pm100 Feb 04 '16 at 01:44
  • "I know fire is hot and my hand cannot stand such heat. So why do I burn my hand when I hold it into open fire?" - So you do something wrong, know it is wrong, ignore warnings and now wonder why it does not work**??** – too honest for this site Feb 04 '16 at 01:49
  • A couple of points: (1) Trying to predict undefined behavior isn't something you should be posting here for help with, and (2) That having been said, my guess would be that your `swap` function is probably swapping the low-order half of the doubles (little-endian), or high-order (big-endian). In the former case (e.g., x86), you will be swapping the low-order mantissa bits and may not notice the change (if you force your `printf` to show many digits, you may notice it). – Tom Karzes Feb 04 '16 at 01:49
  • @Olaf No, I'm asking why, from all the more probable things, this happens. – YoTengoUnLCD Feb 04 '16 at 01:52
  • Think about the meaning of the word **undefined**, like in _undefined behaviour_. – too honest for this site Feb 04 '16 at 02:03

3 Answers3

2

Your program invokes undefined behavior, so any behavior is possible. Compilers aren't required to detect and reject this, although good compilers will often warn about suspicious code like this.

The likely reason it doesn't crash is that the code that's generated for the swap function doesn't really care about the values in the variables, because it's not performing any arithmetic on them. Since it's just copying from one variable to another, it just copies the memory as bytes -- it's not really much different from:

memcpy(&aux, x, sizeof(float));
memcpy(x, y, sizeof(float));
memcpy(y, &auz, sizeof(float));

What this is doing is swapping 32 if the 64 bits of the two doubles. The reason you don't see any change is that these are the low-order bits of the value, and they get rounded away.

This demo shows the internal representation of the two values in hex before and after swapping (using more type-punning with printf).

http://ideone.com/boN3Df

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • although it is interesting to investigate why the function call seems to leave a and b unaffected. I suggest that OP try to work it out – pm100 Feb 04 '16 at 01:46
  • @pm100 That's exactly what I'm asking... I know this is "undefined behavior" but most of the times, what happens has an explanation! – YoTengoUnLCD Feb 04 '16 at 01:52
  • I am suggesting that you try to work it out your self. First look up how doubles and floats are represented - start here https://en.wikipedia.org/wiki/Floating_point – pm100 Feb 04 '16 at 01:55
1

To expand on Barmar's answer: the undefined behavior you're hitting is due to strict aliasing. Strict aliasing is the requirement that a pointer to type A may not be cast to a pointer of type B unless B is byte-sized (or some magic with struct fields, see What is the strict aliasing rule?). I'd be curious to know whether it does swap the elements if you compile with -fno-strict-aliasing.

Edit: to add to that, when you pass the function pointers that violate the strict aliasing rule, the compiler is allowed to do whatever it wants with that function call. In this case, it probably just elided it because the fastest thing it could do in that situation is nothing. Another thing you could check is whether the behavior is different with and without optimizations turned on.

Community
  • 1
  • 1
Haldean Brown
  • 12,411
  • 5
  • 43
  • 58
0

If you try with double with a longer decimal part you can see a difference.

void swap(float * x, float * y)
{
  float aux = *x;
  *x = *y;
  *y = aux;
}

int main(void)
{
  double a = 3.0, b = 5.987654321;
  printf("%f %f\n", a, b);
  swap(&a, &b);
  printf("%f %f\n", a, b);
  return 0;
}

The output is:

3.000000 5.987654
3.000001 5.987652

Standard doubles are 8 bytes and floats are 4 bytes. With your compiler, the swap effectively swap 4 bits of each float (it is still an undefined behavior).