10

I tried to run this code,

int *p;
float q;
q = 6.6;
p = &q;

Though it will be a warning, but i think &q and p are of same size, so p can have an address of q. But when I print &q and p I am getting different output. This is my output

*p =  6.600000 
 q = 0.000000, p = 0x40d33333, &q = 0x7fffe2fa3c8c 

What is that I am missing? And p and &q is same when both pointer and variable type is same.

My complete code is

#include<stdio.h>
void main()
{   
    int *p;
    float q;
    q = 6.6;
    p = &q;
    printf("*p =  %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);
}
Rohit O
  • 330
  • 1
  • 2
  • 10
  • 3
    Please show the code you're using to get that output. – Oliver Charlesworth Apr 03 '13 at 15:05
  • If I get you right, you might want to use a union... – Abu Dun Apr 03 '13 at 15:10
  • 4
    It's `int main(void)`, not `void main()`. `void main()` is useful mostly for detecting books written by authors who don't know C very well. – Keith Thompson Apr 03 '13 at 15:12
  • 1
    You're runing on x86_64? Then what probably happens in `printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);` is that the putative `int` `*p` is passed in a general purpose register, the `double` (the promoted value of `q`) is passed in a floating point register, the two pointers again in general purpose registers. – Daniel Fischer Apr 03 '13 at 20:20
  • 1
    Then `printf` reads the first floating point register for the first `%f` conversion, the second floating point register for the second `%f` [coincidentally that contains 0], the first general purpose register - that contains the bits of the `float` 6.6 - to print `p`, and the second general purpose register (containing the value of `p`) to print `&q`. The register containing the address of `q` remains unread. – Daniel Fischer Apr 03 '13 at 20:20
  • @KeithThompson I know int main(void) should be used always. But in this program it doesn't really matter. My main question is pointer can be of any type, but it will hold the address. Then forget about the size of that datatype, why is address different? And I even know I'm trying to use int* with float, but I'm just experimenting with pointers. – Rohit O Apr 03 '13 at 21:17
  • @DanielFischer yes, you are right. I split the `printf` in to parts first one for `values` and second one for `addresses`. And the problem was with `%f`, what I thought was if `p` contains address of `float` it will still work if I give `%f` in `printf` statement. But it loads data in general purpose register this problem is happening. After I split, I got correct addresses but values are still different. Thanks for the answer. :) – Rohit O Apr 03 '13 at 21:26
  • 4
    @RohitO: If you know `int main(void)` should always be used, then I suggest you *always use it*; at the very least, it will avoid complaints from people like me. As for why `int*` and `float*` are distinct types, I've just added a couple of paragraphs to my answer. – Keith Thompson Apr 03 '13 at 21:27

3 Answers3

10

You need to take compiler warnings more seriously.

C doesn't require compilers to reject invalid programs, it merely requires "diagnostics" for rule violations. A diagnostic can be either a fatal error message or a warning.

Unfortunately, it's common for compilers to issue warnings for assignments of incompatible pointer types.

void main()

This is wrong; it should be int main(void). Your compiler may let you get away with it, and it may not cause any visible problems, but there's no point in not writing it correctly. (It's not quite that simple, but that's close enough.)

int *p;
float q;
q = 6.6;

That's ok.

p = &q;

p is of type int*; &q is of type float*. Assigning one to the other (without a cast) is a constraint violation. The simplest way to look at it is that it's simply illegal.

If you really want to do this assignment, you can use a cast:

p = (int*)&q; /* legal, but ugly */

but there's rarely a good reason to do so. p is a pointer to int; it should point to an int object unless you have a very good reason to make it point to something else. In some circumstances, the conversion itself can have undefined behavior.

printf("*p =  %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);

The %f format requires a double argument (a float argument is promoted to double in this context so float would be ok). But *p is of type int. Calling printf with an argument of the wrong type causes your program's behavior to be undefined.

%p requires an argument of type void*, not just of any pointer type. If you want to print a pointer value, you should cast it to void*:

printf("&q = %p\n", (void*)&q);

It's likely to work without the cast, but again, the behavior is undefined.

If you get any warnings when you compile a program, don't even bother running it. Fix the warnings first.

As for the question in your title, pointers of type int* and float* are of different types. An int* should point to an int object; a float* should point to a float object. Your compiler may let you mix them, but the result of doing so is either implementation-defined or undefined. The C language, and particularly many C compilers, will let you get away with a lot of things that don't make much sense.

The reason that they're distinct types is to (try to) prevent, or at least detect, errors in their use. If you declare an object of type int*, you're saying that you intend for it to point to an int object (if it's not a null pointer). Storing the address of a float object in your int* object is almost certainly a mistake. Enforcing type safety allows such mistakes to be detected as early as possible (when your compiler prints a warning rather than when your program crashes during a demo for an important client).

It's likely (but not guaranteed) that int* and float* are the same size and have the same internal representation. But the meaning of an int* object is not "a collection of 32 (or 64) bits containing a virtual address", but "something that points to an int object".

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 5
    Since you're throwing the book at him, reading `*p` violates strict aliasing, so it's UB in itself regardless of what you do with the value. – Steve Jessop Apr 03 '13 at 15:24
  • 3
    `p = (int *) & q;` is not always legal. C 2011 (n1570) 6.3.2.3 7 says the behavior is undefined if the result is not correctly aligned. It will be on typical current platforms, so this is okay if you are planning your target accordingly but not if you are planning for standard C or want to understand the semantics of the C specification. – Eric Postpischil Apr 03 '13 at 15:36
  • @EricPostpischil: Correct -- except that it's not clear what "legal" means in this context (the standard never uses that word). I've updated my answer. – Keith Thompson Apr 03 '13 at 17:11
  • @KeithThompson When will be the size of `int*` and `float*` may differ? I thought address will always be of fixed size, regardless of datatype. Or is there any case in which `int` is stored in different memory and `float` is stored in different memory.? – Rohit O Apr 03 '13 at 21:36
  • 1
    @RohitO: It's very likely that `int*` and `float*` will be the same size -- but the C standard doesn't require it. Another example: there are systems that are word-addressed, so a machine address points to a full word (say, 4 or 8 bytes) -- but a `char*` consists of a machine address plus an offset within the word. You're not likely to run into such a system -- but you're also not likely to need to *care* about the relative sizes of `int*` and `float*`. If you need the size of an `int*` just write `sizeof (int*)` (or, more likely, `sizeof some_pointer_object`). – Keith Thompson Apr 03 '13 at 21:45
  • Downvoter: If there's a problem with this answer, please leave a comment explaining it. – Keith Thompson Jun 11 '14 at 15:44
  • Implementations may accept `void main()`. It's only "wrong" in the sense that some other compiler may not accept it , this is a different sort of wrong to a constraint violation which must be diagnosed by all compilers – M.M Mar 27 '18 at 11:43
  • @M.M: I acknowledged that it's not quite as simple as it being "wrong". The details have been discussed at length elsewhere. (The problem isn't just that some compilers accept it and others don't. For compilers that don't diagnose it but don't document that they accept it, the behavior is undefined.) – Keith Thompson Mar 27 '18 at 15:49
9

You're getting undefined behaviour, because you're passing the wrong types to printf. When you tell it to expect a float, it actually expects a double - but you pass an int.

As a result it prints the wrong information, because printf relies entirely on the format string to access the arguments you pass it.

teppic
  • 8,039
  • 2
  • 24
  • 37
0

In addition to what is said by teppic,

Consider,

int a = 5;
int *p = &a;

In this case we indicate to the compiler that p is going to point to an integer. So it is known that when we do something like *p , at runtime, the no. of bytes equal to size of an int would be read.

If you assign address of a datatype occupying x number of bytes to a pointer of which is declared to hold the address of datatypes of fewer bytes than x, you read the wrong number of bytes when using the indirection operator.

Suvarna Pattayil
  • 5,136
  • 5
  • 32
  • 59
  • 1
    Who says `float` is bigger than `int`? It's allowed to be, but probably isn't. – Steve Jessop Apr 03 '13 at 15:21
  • @Steve Yes, I agree its not always the case. But some systems it is that way. By bigger i meant occupies more bytes. Maybe my example isn't the best one, to put my point through. I just wanted to indicate that we define the type of pointer for a reason [just like we have to typecast `void` pointer before using indirection on it] – Suvarna Pattayil Apr 03 '13 at 15:23
  • I suggest that the size is not the point. I've worked on systems where `sizeof (int) == sizeof (double)`; the program's behavior is equally undefined on such a system. You risk giving the impression that mixing pointer types like this is ok if the sizes happen to match. And `p` specifically needs to point to an `int`, not just any integer. – Keith Thompson Apr 03 '13 at 15:27
  • Actually, i wanted to indicate that its not safe to mismatch pointer types and the datatype it points to. – Suvarna Pattayil Apr 03 '13 at 15:53