4

Very new to C here and I think I just barely grasp the concept of pointers, but the syntax is a bit confusing so I'm having trouble trying to understand what this expression x = (char *) &a; means.

Rest of function for reference:

#include<stdio.h> 
int main() 
{ 
   int a; 
   char *x; 
   x = (char *) &a; 
   a = 512; 
   x[0] = 1; 
   x[1] = 2; 
   printf("%d\n",a);   
   return 0; 
}

More specifically, why is it necessary to write x = (char *) &a; instead of just x = &a;? What does the added (char *) do to alter the expression?

doctopus
  • 5,349
  • 8
  • 53
  • 105
  • Might I ask, where did you get this code from? – Bathsheba Mar 08 '18 at 13:47
  • @octopus There is no implicit conversion from the type int * that is the type of the expression &a to the type char * that is the type of x. – Vlad from Moscow Mar 08 '18 at 13:48
  • it allows to access the individual bytes of the `int`. Endian-dependent it is. – Jean-François Fabre Mar 08 '18 at 13:49
  • Possible duplicate of [How do C/C++ compilers handle type casting between types with different value ranges?](https://stackoverflow.com/q/340413/608639) and [What does a C cast really do?](https://stackoverflow.com/q/13746136/608639) – jww Mar 08 '18 at 14:48
  • Accessing the `int`, after it was modified using `char*` is *undefined behavior*. – Eugene Sh. Mar 08 '18 at 14:57
  • @EugeneSh. What makes it UB? – Hatted Rooster Mar 08 '18 at 15:06
  • @EugeneSh. Are you sure it is? Because I think `a` has a declared type `int`. From what I understand, it should be okay. – Ajay Brahmakshatriya Mar 08 '18 at 15:07
  • @SombreroChicken dbush have updated their answer about it. Technically, after you have written it using `char*`, the object is becoming of type `char` array, and reading it as `int` would be a strict-aliasing rule violation. – Eugene Sh. Mar 08 '18 at 15:07
  • @EugeneSh. That would have been true for malloc'd objects. But since `a` here has a declared type, it won't change type to char array and reading it as an integer won't violate strict aliasing. Unless ofcourse int has a trap representation and it is set to that. – Ajay Brahmakshatriya Mar 08 '18 at 15:13
  • @AjayBrahmakshatriya You are right.It has a declared type, and it's *stored* value can be accessed by an lvalue of a character type. Yet I can't find a reference regarding the *storing* of the value. – Eugene Sh. Mar 08 '18 at 15:37
  • @EugeneSh. regarding the storing, I think you can always write to an object using an lvalue of `char` type. And you can always read (or write) it with an lvalue of its declared type (if it exists). – Ajay Brahmakshatriya Mar 08 '18 at 15:50
  • @AjayBrahmakshatriya Looks like a moot point like the whole strict-aliasing thing.. It can definitely lead to UB if resulting in trap. But I don't know if it is enough to say it is UB in general... – Eugene Sh. Mar 08 '18 at 16:07

2 Answers2

8

It's a cast. It tells the compiler that it should interpret &a as a char* instead of a int* which is it's actual type.

Not making this cast would get you a compilation error as the types don't match, you're basically telling the compiler "I know what I'm doing and I'm sure this is a char*" thus allowing you to approach type X as if it was type Y.

Normally, casting a pointer of type X to Y and trying to dereference it through type Y would violate the strict aliasing rule but in this case because we're aliasing through char* it is allowed.

In this context it allows you to access the individual bytes of the int (by x[]), do note that the result will be different depending on the endianness of the machine (big or little).

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • 1
    aaah with such a good answer, I was wondering. You got c++ gold that's cheatin' :) – Jean-François Fabre Mar 08 '18 at 13:59
  • 1
    @Jean-FrançoisFabre I'm extremely sorry, it won't happen again. – Hatted Rooster Mar 08 '18 at 14:00
  • You could be more clear about what a cast is: A conversion of a value from its original type to the one in the parentheses. There is nothing magic about it; it has exactly the same semantics as an implicit conversion in an assignment, say of an int to a float (where one would be allowed); it's just that some conversions must be made explicit for safety, as -- at least for the past 30 years or so -- in the OP's case. Such a conversion may (like in int -> float) or may not (like here, probably) alter the physical representation of the value. – Peter - Reinstate Monica Mar 08 '18 at 14:06
  • 1
    @SombreroChicken I'll be watching you. – Jean-François Fabre Mar 08 '18 at 14:10
  • thanks for the explanation @SombreroChicken! Just a quick question though - is this typecasting only needed with char? – doctopus Mar 08 '18 at 19:08
  • @doctopus No, for any type `X` you want the compiler to treat as `Y`. – Hatted Rooster Mar 08 '18 at 20:01
3

This is a type cast. It allows explicit conversion from one type to another.

If you just did this:

x = &a;

You would be trying to assign a int * to a char *. Converting between the two types is not normally allowed, and the compiler will typically give a warning if you do so.

The cast explicitly tells the compiler to treat an expression as a different type. In this case:

 x = (char *) &a;

The cast says to explicitly convert &a, which has type int *, to an expression of type char *. This expression can then be assigned to x with no warnings.

In most cases, converting from one pointer type to another invokes implementation or undefined defined behavior, however converting to a char * is allowed, as it allows you to access the individual bytes of a datatype consisting of multiple bytes. What you can't do however is use a char * which points to an object of a different type to write to that object. Doing so risks creating a trap representation, and subsequently attempting to read the original datatype would invoke undefined behavior.

dbush
  • 205,898
  • 23
  • 218
  • 273