-1

I wrote a simple program below which calculates the sum and return the result to main(). The main() then calculates the average. SumOfArray() expects float but I am passing an array of integers. I expected that the integers would be converted into float type implicitly. i.e 35 would be passed as 35.0 but to my surprise it was 0. the short explanation from whatever I could find is that the behavior is undefined because in this case the typecast happened not from (int)->(float) but (int*)->(float*)

However why does the function SumOfArray() see 0 instead of actual integers if it is reading from the same location. I checked this by printing the address of array both in main() and SumOfArray(). They were same. Is there anything specific about this mentioned in ISO C standards? The size of integer and float on my machine is 4 bytes so I expected it would read same values albeit with loss of precision.
Following is my program. I printed the values of array passed to the function in SumOfArray. I am using gcc compiler , codeblock ide compiling for x86_64. Tried this on debian as well.

#include<stdio.h>

float SumOfArray(const float array[],unsigned int size)
{
  float sum=-1.0;
  if(size!=0&&array!=0)
  {
    sum=0;
    for(int i=0;i<size;i++)
    {
      sum+=array[i];
      printf("sum in sum of array is%f\n",sum);
      printf("array[%d] is %f\n",i,array[i]);
    }
  }
  return sum;
}

int main()
{
  int gradesI[]={35,56,88,23,45,99};
  unsigned int sizeI = sizeof(gradesI)/sizeof(int);
  
  float sumI = SumOfArray(gradesI,sizeI);
  printf("the sum of is %f\n",sumI);
  
  float average = sumI/sizeI;
  printf("the average is %f\n",average);
  
  return 0;
}

- *output*
sum in SumOfArray is 0.000000
**array[0] is 0.000000**
sum in SumOfArray is 0.000000
**array[1] is 0.000000**
sum in SumOfArray is 0.000000
**array[2] is 0.000000**
sum in SumOfArray is 0.000000
**array[3] is 0.000000**
sum in SumOfArray is 0.000000
**array[4] is 0.000000**
sum in SumOfArray is 0.000000
**array[5] is 0.000000**
the sum of is 0.000000
the average is 0.000000
Jane
  • 17
  • 2
  • 7
    You're passing a pointer. It's not going to create a temporary array, populate it with integers, and pass its address as the function argument. You need to do that yourself. As it is now, you have a pointer type mismatch which should have generated a compile-time warning. Don't even bother trying to run the code until you fix that. It can't possibly work. – Tom Karzes Jul 11 '23 at 12:17
  • You are correct that using an int where a float is expected will cause automatic conversion. A pointer is a different matter and should have given a warning or error when compiling. Fix all warnings and errors in your code. – stark Jul 11 '23 at 12:26
  • You should have gotten a compiler warning that should be considered as an error: https://godbolt.org/z/58GdT7Gj6. Consider most (if not all) compiler warnings as errors, especially those containing the words _incompatible_ and _implicit_. – Jabberwocky Jul 11 '23 at 12:27
  • 1
    The int value 35 (0010 0011 binary) interpretet as float in IEEE 754 is some very small value (~5e^-44). Unless you print 44 digits after the decimal point, it will read as 0.0000. Note that this interpretation is based on assumptions, and Undefined Behaviour is undefined - you cannot rely on this! – king_nak Jul 11 '23 at 12:32
  • @king_nak That assumes the `float` has the same endianness as the `int` and many other things that may or may not be the case. – 12431234123412341234123 Jul 11 '23 at 12:36
  • 2
    @12431234123412341234123 That's true. Maybe I should have highlighted more the part "this interpretation is based on assumptions", and that there are a lot of those (it's not said that it is IEEE754, too)... As this is UB land, all bets are off anyway. I just found it helpful for understanding, why a certain behaviour is observed on a concrete machine/run, even if it cannot be relied on – king_nak Jul 11 '23 at 12:42
  • 1
    Yes all of you are right. I realized I am passing int* instead of int ( i mentioned that in the details). I was curious about what does C standard actually say about it and why it was reading all 0s. @king_nak it does make sense. – Jane Jul 11 '23 at 12:45
  • Yes, it makes sense, IF the assumtions are valid. But from the C Standard perspective: it's Undefined Behaviour. You cannot predict anything at all, never, and it may change at any time, machine, or "just because". This is the most important takeaway. In theory, the compiler can assume UB never happens, and change the compiled code accordingly (this is what caused a linux kernel vulnarbility by a GCC update: https://lwn.net/Articles/342330/) – king_nak Jul 11 '23 at 12:57

2 Answers2

2

SumOfArray() expects float

No, it expects pointer to float.

but I am passing an array of integers.

No, you are passing pointer to int, by virtue of the automatic conversion (decay) of array to pointer.

I expected that the integers would be converted into float type implicitly.

If the parameter in fact had type float, and the argument had type int, then there would indeed be an automatic conversion from the latter to the former, but that is not your case. The C language requires an explicit conversion for the mismatched pointer types actually involved here, but some compilers will perform it implicitly as an extension. If yours is doing that without even issuing a diagnostic message, however, then you should either turn up its warning level or get a better compiler.

And the reason for an explicit cast and for expecting a diagnostic is that converting the pointer type has no effect on the object to which the pointer points. After such a conversion, you have a pointer of type float * that points at an object of type int. Accessing the int via the float * produces undefined behavior.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thank you. I realized I was sending int* instead of int. I also understood that it is reading the binary of 35 as float therefore it is printing all 0s. – Jane Jul 11 '23 at 12:57
  • @jane, since you asked about what the language spec has to say, I want to re-emphasize that the spec explicitly denies the "therefore" in your comment. Again, attempting to read an `int` via a pointer of type `float *` produces *undefined behavior*. That can take a form as benign as reinterpreting the bits of the pointed-to object as if they were the bits of a `float` (which is different from a value conversion), but that is by no means the only possibility, neither in theory nor in practice. – John Bollinger Jul 11 '23 at 13:06
  • Got it. Thanks for pointing this out. – Jane Jul 11 '23 at 13:13
0

You are not passing a int but a pointer to int (decayed from a array of int) and you don't expect a float but a pointer to float.

What you have in memory gradesI:

+------+------+------+------+-
| int  | int  | int  | int  | ...
+------+------+------+------+-

When you call a function with gradesI as argument, it decays to a pointer to int, a pointer that points to gradesI[0].

What SumOfArray() expects the pointer pointing to in memory

+------+------+------+------+-
| float| float| float| float| ...
+------+------+------+------+-

What you ask is why the int array isn't converted automatically to a float array. There are multiple reasons:

  • In c only the argument itself (i.e. the pointer) gets converted. A pointer to int shouldn't be implicitly converted to a pointer to float, because you can't access a int through a float pointer (when you try it you get undefined behaviour).

  • The compiler would have to generate a new temporary array and passing a pointer to the new temporary array. But that would break a lot of code since we expect the pointer in the function to point to the same location as in the caller.

  • The compiler doesn't know how many int's it should convert to float.

  • Even if the compiler knows that, it would be time consuming.