0

I was testing if it was possible to have int and float values in the same array, and I wrote this code:

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

int main()

{
    int v[4]={0,0,0,0};
    *((float*)(&v[1]))=45.6;
    printf("%f\n",*((float*)(&v[1])));

    printf("%f\n",v[1]);
    return 0;



}

I was expecting to have 45.599998 and 0 or have the same values on both printf but i get different results: 45.599998 45.599983 Why?, What is happening?

EDIT
By the way, I want to emphasize that for this question I'm not as interested in alternatives to do this, I'm more interested in understanding why is not working.

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 3
    The code has two different types of undefined behavior. So there's no point in try to understand or predict the output. In can be anything. Compiling with `-Wall -Wextra -Werror` would at least force you to fix one of the errors. – user3386109 Oct 14 '20 at 16:47
  • I added part of the content of your comment under my answer into your original post because I think it is very relevant to let others know what you are really trying to learn. – ryyker Oct 14 '20 at 18:14

3 Answers3

2

In the example code, as pointed out in the comments, undefined behavior is invoked. (In this case I believe strict aliasing is being violated.)

"[is it] possible to have int and float values in the same array"

Not in C, an array of any type can contain only one type. However, if a variation of this arrangement will work for you, both types can be defined in a single instance of a compound type, such as an array of struct. Eg:

typedef struct {
   int iVal;
   float fVal;
} val_s;

val_s val[10];

Now you have an array of 10 elements of type val_s, for which each element includes both int and float type members.

Another variation of compound type in C (which has been pointed out to me in comments that may be more of what you are looking for) is a built-in C type that allows multiple types to share the same memory, is a union. The caveat is that it is up to the programmer to keep track of which of the members was last written to, as only one member can contain a value at any given time...

Example:

typedef union {
   int iVal;
   float fVal;
} val_s;

val_s val;
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 1
    Or using a `union` instead of a `struct` would be closer to what the OP says they want to do, I think. – John Bollinger Oct 14 '20 at 17:05
  • @JohnBollinger - would you like to use that in an answer? If not, I will be glad to add it here. – ryyker Oct 14 '20 at 17:08
  • 1
    I am writing an answer, but it focuses on a different aspect of the question. Feel free to add the `union` alternative to this answer. – John Bollinger Oct 14 '20 at 17:09
  • Than you for the answers. I'm not really interested in alternatives to do that, Im more interested in understanding why is not working.So if i understand correctly the problem is with printf("%f\n",v[1]);, but the upper code is ok?. – user14450516 Oct 14 '20 at 17:37
  • @user14450516 - - with regard to _"I'm not really interested in alternatives to do that, Im more interested in understanding why"_..., That's very fair. Next time if you place that as your objective in the original post, it would be good :) I believe if you look at John Bollinger's answer, you will then understand clearly the answer to your question here. (why it is not working...) What you are trying to do is riddled with ***undefined behavior*** as I have briefly pointed out in my answer above. Because of this, your code can do anything, but you can trust none of what it does. :) – ryyker Oct 14 '20 at 18:15
1

I was expecting to have 45.599998 and 0 or have the same values on both printf but i get different results: 45.599998 45.599983 Why?, What is happening?

Suppose we eliminate some of the undefined behavior in your code by using this variation:

#include <string.h>
#include <stdio.h>

int main() {
    int v[4]={0,0,0,0};
    float f = 45.6;
    memcpy(&v[1], &f, sizeof f);

    printf("%f\n", f);
    printf("%f\n", v[1]);

    return 0;
}

This still has undefined behavior because format directive %f is not properly type-matched with v[1], but otherwise it is ok as long as float is not larger than int, and int has no trap representations (both of which hold for most C implementations).

Even in the likely event that the values of f and v[1] have identical byte-sequence representations, the difference between their types has an important consequence with respect to this code. The variable arguments of a variadic function such as printf are subject to the "default argument promotions". These leave values of type int unchanged, but they promote floats to type double. Thus, if your float and double differ in practice, which they typically do, then

  1. printf receives different argument values in the two cases, even when only the byte sequence of each argument is considered, and
  2. in the v[1] case, printf probably does not receive a wide enough value.

So if you want to indulge in the dubious practice of hypothesizing about what the program actually does in this case of undefined behavior, then one of the more likely possibilities is that in the v[1] case it looks at the byte sequence of a float, combined with some additional random bytes that happen to be laying around in memory, interprets them as if they were the bytes of a double, and, by stroke of luck and the details of the chosen test value, it comes up with a numeric value close to, but not exactly matching the double to which your float was promoted.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • _"indulge in the dubious practice of hypothesizing,...,undefined behavior..."_ Love it. – ryyker Oct 14 '20 at 17:49
  • So if instead of using int and float i use long long and double the undefined behavior is fixed? – user14450516 Oct 14 '20 at 18:24
  • No, @user14450516, that would not fix the UB, even supposing that `long long` and `double` are the same size, which is not guaranteed. Undefined behavior arises from the mismatch between the argument type and the field directive, notwithstanding the size of the type. If the integer type used were the same size as a `double` then the chances would be better that the UB would manifest as the same output being printed twice, but that result is not even conditionally guaranteed. – John Bollinger Oct 15 '20 at 00:49
0

Using a floating point specifier and passing an integral value to satisfy that argument in the format string is undefined behavior, which is to say, all bets are off.

Lots of systems pass integral arguments and floating point arguments in completely different ways; some don't. 'Why' cannot be answered fully. You happen to be on a system whose calling convention doesn't seem to pass integral and floating point arguments differently.

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85