4

I'm learning how pointers works, but I don't understand one thing in this code. Returning int in void* function works like a charm, but returning float does'nt.

#include <stdio.h>

void* square (const void* num);

int main() {
  int x, sq_int;
  x = 6;
  sq_int = square(&x);
  printf("%d squared is %d\n", x, sq_int);

  return 0;
}

void* square (const void *num) {
  int result;
  result = (*(int *)num) * (*(int *)num);
  return result;
}
#include <stdio.h>

void* square (const void* num);

int main() {
  float x, sq_int;
  x = 6;
  sq_int = square(&x);
  printf("%f squared is %f\n", x, sq_int);

  return 0;
}

void* square (const void *num) {
  float result;
  result = (*(float *)num) * (*(float *)num);
  return result;
}
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
gunhasiz
  • 118
  • 1
  • 8
  • 7
    *"Returning int in void * function works like a charm"* If you didn't get compiler warnings, then please check that you have enabled them. – user694733 May 24 '19 at 10:32
  • 2
    First of all why are returning pointer? For your problem you can just return `int` or `float`. – kiran Biradar May 24 '19 at 10:33
  • But why does int works and float doesn't? – gunhasiz May 24 '19 at 10:33
  • Because it is intresting thing – gunhasiz May 24 '19 at 10:34
  • 6
    It is your luck that it is working even for `int`. You have undefined behavior. – kiran Biradar May 24 '19 at 10:34
  • 2
    None of this "works like a charm" because neither is valid C language. See [“Pointer from integer/integer from pointer without a cast” issues](https://stackoverflow.com/questions/52186834/pointer-from-integer-integer-from-pointer-without-a-cast-issues). – Lundin May 24 '19 at 10:41
  • 1
    @user694733 One need not enable warnings for this on a compliant compiler. If you don't get any warnings, your compiler is broken and should not be used. – Lundin May 24 '19 at 10:43
  • 4
    "works like a charm" I think you have misspelled "I've got a compiler warning and ignored it because I didn't understand what it means and why it is important". Hope it helps. – n. m. could be an AI May 24 '19 at 10:45
  • @kiranBiradar Or, bad luck. It's good luck that is created problem with `float`, otherwise, it'd have been a wrong learning for OP. – Sourav Ghosh May 24 '19 at 10:55
  • @SouravGhosh Yep! I agree. – kiran Biradar May 24 '19 at 11:23
  • 1
    As for why it works: undefined behaviour doesn't mean it produces an error, it can just as well appear to work "like a charm" because _it can do anything at all_. As for speculation as to why this particular case appears to work, you are returning a pointer, and a pointer in itself is basically an integer (the memory address), so you are able to convert between pointer and integer interchangeably. – Arkku May 24 '19 at 11:44

3 Answers3

7

Both of your functions invoke undefined behaviour, as the return type and type of the expression in the return statement do not match.

As on why it seem to work, read about undefined behaviour.

That said, turn up the compiler warnings where you treat all warnings as error, and with that setting this code should not even compile to produce a binary, as it contains a constraint violation which is supposed to produce a diagnostic (warning).

Related notes:

Quoting from C11, chapter §6.8.6.4

[...] If the expression has a type different from the return type of the function in which it appears, the value is converted as if by assignment to an object having the return type of the function.

and regarding simple assignment, from chapter §6.5.16.1, simple assignment:

Constraints

One of the following shall hold:

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

So, you cannot legally assign an int or float to a pointer type - this is a constraint violation.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • It is a C language violation, the code is not allowed to compile without the compiler giving a diagnostic message. – Lundin May 24 '19 at 10:42
  • @Lundin Just updated that, hope it should cover that part. – Sourav Ghosh May 24 '19 at 10:43
  • "cannot legally assign an int or float to a pointer type" --> Hmmm. C has "An integer may be converted to any pointer type" §6.3.2.3 5 and counter example: `int *x = 0;` – chux - Reinstate Monica May 24 '19 at 11:29
  • @EricPostpischil Even if the warning is treated as error? – Sourav Ghosh May 24 '19 at 12:57
  • @EricPostpischil OK, I'll re-word as suggested. Also, so you mean to say, there's nothing called conforming compilers? – Sourav Ghosh May 24 '19 at 13:04
  • There are conforming C implementations (at least in theory; in practice everything has bugs and misses something) but not strictly conforming C implementations. A *strictly conforming* program is one whose behavior is fully defined by the standard—it does not use any unspecified, undefined, or implementation-defined behavior and stays within minimum implementation limits. A *conforming* implementation is one that accepts any strictly conforming program. A *conforming* program is one that is acceptable to at least one conforming implementation. – Eric Postpischil May 24 '19 at 15:14
  • So a strictly conforming program is one that is fully “portable”. A conforming program is sort of “anything you can make in C, even if it uses extensions and undefined behavior.” A conforming implementation has to support the core of C (all strictly conforming programs) and may support extensions. That includes that it may define behavior not defined by the C standard. The standard does not define anything called a strictly conforming implementation. I suppose you could define that as an implementation that accepts only strictly conforming programs, but it would be practically useless. – Eric Postpischil May 24 '19 at 15:17
2

The problem is that you try to convert a float to a pointer.

C does not specify anything about the format of a pointer. This is because C tries to abstract all possible architectures and some architectures have very uncommon ways to represent pointers. For example, on lisp machines the memory locations, hence the pointers are represented in segment:offset). The same, on Harvard architectures it is separated the zone of code of the zone of data and the ways the pointer encode these different zones. C makes only the distinction between pointer to function vs. pointer to object but says nothing about the meaning of each bit from pointer object.

The fact that on your computer the integer is well converted is just a chance. If your computer had 64 bus addresses and integers on 32 and the pointer returned makes some sense and seems "equal" with the integer, this means only that the architecture may have the same representation of integer values as for pointers.

Now, in your code the float is converted to a pointer and from pointer back to float. The representation of floats is defined as sign/exponent/mantissa but the pointer representation is not defined and undefined behavior can happen.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
1

If you still want to want this to work, you should return a pointer to a float. But doing this in a function involves some heap allocation. Code could we look like this. I didn't include any checks if there is a real float returned or not. This is your task

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


void* square (const void* num);

int main() {
  float *x, sq_int;
  float value = 6;
  x = square(&value);
  printf("%f squared is %f\n", value, *x);
  free(x);
  return 0;
}

void* square (const void *num) {
  float *result = malloc(sizeof(float));
  *result = (*(float *)num) * (*(float *)num);
  return ((float*)result);
}
ckruczek
  • 2,361
  • 2
  • 20
  • 23