0

I can't wrap my head around why does the following lines give ``initialization from incompatible pointer type'' in gcc:

int num = 10;
int *p = (char *) #

Why isn't it clear to the compiler that I want this (when I do this, warning message goes away):

int num = 10;
int *p = (int *) (char *) &num ;

If the following is OK:

char eu = (short) 10;

And we don't need to do that:

char eu = (char) (short) 10;

then why do we need to add a base type explicitly in pointer definition?

user1042840
  • 1,925
  • 2
  • 16
  • 32
  • 6
    Because the rules for the automatic conversion of integer types are different from those for pointers. – us2012 Sep 25 '13 at 23:42
  • But there is no automatic conversion in `char eu = (short) 10;'? It's explicit, too – user1042840 Sep 25 '13 at 23:45
  • 2
    @user1042840: The conversion back from `short` to `char` is implicit. – Oliver Charlesworth Sep 25 '13 at 23:49
  • There's a constraint violation for the pointer assignment (6.5.16.1/1), so the compiler is *required* to produce a diagnostic message (5.1.1.3/1). There's no constraint violation for the assignment of a `short` to a `char` (which involves a conversion, also w/o constraint violation here), so there's no requirement for a diagnostic message. The compiler *could* produce a diagnostic message for this narrowing, but it chooses not to. (N.B. there's no actual narrowing here, as `10` fits into a `short` as well as in a `char`) – dyp Sep 26 '13 at 00:22

3 Answers3

5

Well, it's simply the way the language is defined: Object pointers of different types are not implicitly convertible among each other. You need the explicit cast.

In most cases it's undefined behaviour to dereference a pointer of the wrong type, so requiring you to be explicit about this sort of cast is a very sane precaution.

It's a sign of good design that easy things are easy (e.g. int * p = #), and dodgy things are verbose so that it's clear that you're doing something odd.

(Note that in C (but not C++) you can implicitly convert via void pointers: int * p = (void *)#)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Why do you say `implicitly convertible'? Isn't `int *p = (char *) #' explicit? – user1042840 Sep 25 '13 at 23:47
  • 3
    @user1042840: You cannot convert a `char*` implicitly to an `int*`. – Kerrek SB Sep 25 '13 at 23:49
  • 1
    "Object pointers of different types are not implicitly convertible among each other. You need the explicit cast." This is true for C++, but I'm not sure this is true for C, see 6.3.2.3/7. It's a constraint violation for the assignment, though, see 6.5.16.1/1. – dyp Sep 25 '13 at 23:54
  • 3
    @DyP: 6.5.4.3: "Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast." The permission in question is the allowance for implicit conversion to and from `void *`. – Carl Norum Sep 26 '13 at 00:04
  • @CarlNorum That's just another constraint violation. 6.3.2.3/7 says "If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer." As far as I understand the specifications, this (`p = (char*)&num`) isn't even UB, as it's just a violation of constraints 6.5.16.1/1 and 6.5.4/3. – dyp Sep 26 '13 at 00:08
  • @Carl Norum - I always want to look up final explanation in C standard but I still have some problems with searching in it. Is the passage you quoted an explanation for my question? – user1042840 Sep 26 '13 at 00:33
2

C won't implicitly convert from one pointer type to another, unless one of those is a void *. Such a conversion may not, in general, be possible.

For example, supposed that on a given platform, pointers to integers must be aligned to a four-byte boundary. You wouldn't want the compiler to silently try to convert a char *--which might not be aligned--to an int *.

By putting in an explicit cast, you tell the compiler, "trust me, I know what I'm doing."

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • "You wouldn't want the compiler to silently try to convert a char *--which might not be aligned--to an int *" - I think this would cause UB because char* that takes one byte would have to be widened to at least 4 bytes, especially dereferencing or writing to such a new pointer would be dangerous because we don't know what is stored in this memory at the moment – user1042840 Sep 26 '13 at 00:29
0

char eu = (short) 10; is OK because it's assigning a value. Operations on a value always preserve it if the destination type can represent that value, or transform the value in a defined/meaningful way (sign-extension, truncation, modulo 2n), except for a few cases like special values in floating-point types when assigning to integer types. Therefore the C standard allows these to be done implicitly, although compilers may throw warnings at you

OTOH a pointer relates directly to the underlying memory representation. You can't access a double as an int by casting double* to int* for example. Therefore automatic pointer casting is disallowed to prevent unexpected results. For example an unaligned access may cause an exception or slow down the operation, so implicitly converting char* to int* is forbidden. Accessing a type using a different pointer type is called aliasing which invokes UB and compilers also use that for many optimizations. See What is the strict aliasing rule?

phuclv
  • 37,963
  • 15
  • 156
  • 475