What is the meaning of (int*) &i?
char i;
int* p = (int*) &i;
...
*p = 1234567892;
...
If it was * &i, I would understand. But in this case, this an "int" in there.
&i : means to take the address of i (which is a char*)
(int*)&i : casts that pointer to be a pointer to integer (which is bad/wrong to do, but you told the compiler to do it so it won't even give a warning)
int* p = (int*)&i; : a statement that says to store the pointer of i in p (and cast it too: the compiler won't even complain)
*p = 1234567892; : write this value, which is several bytes to the base location pointed to by p (which although p thinks it points to an int, is to char!). One of those bytes will end up in i, but the others will over write the bytes neighboring i.
The construct (int *) &var, where var is a char, takes a pointer to var, and then converts it to a pointer of a different type (namely int). The program later writes an int value into the pointer. Since the pointer actually points to a char, an int value does not fit, which triggers undefined behavior, which is a fancy name for "literally anything (that your computer can physically accomplish) could happen -- this program is buggy".
EDIT: As requested, some standardology to explain why this program has undefined behavior. All section references below are to N1570, which is the closest approximation to the official text of C2011 that can be accessed online for free.
As a preamble, when reading the text of the C standard, you need to know that the word "shall" has special significance. Any sentence containing the word "shall" imposes a hard requirement on either the program, or the compiler and runtime environment; you have to figure out which from context. There are two kinds of hard requirements on the program. If a "shall" sentence appears in a "constraints" section, then the compiler is required to diagnose violations (§5.1.1.3) (the standard never flat out says that a program must be rejected, but that's the usual line drawn between hard errors and warnings). If a "shall" sentence appears somewhere else, then the compiler isn't required to diagnose it, but a program that violates the requirement has undefined behavior (§4p1,2). Sometimes the text says "If X, then the behavior is undefined" instead; there's no difference in the consequences.
First off, the conversion (int *) &var converts char * to int *, which is explicitly allowed by §6.3.2.3p7 if and only if the value of the pointer-to-char is properly aligned for an object of type int.
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.
There's nothing in the code shown that would ensure that var is aligned appropriately for an int, so the program might already have triggered undefined behavior at this point, but let's assume it is aligned correctly. Saving a value of type int * into a variable declared with that type is unproblematic. The next operation is *p = integer_literal. This is a write access to the stored value of the object var, which must obey the rules in §6.5p6,7:
The effective type of an object for an access to its stored value is the declared type of the object, if any. [... more text about what happens if the object has no declared type; not relevant here ...]
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
a character type
For simple arithmetic types like int and char, compatible type means the same type after stripping typedefs. (The exact definition is spread over §§ 6.2.7, 6.7.2, 6.7.3, and 6.7.6.) What matters for this analysis is simply that the declared type of var is char, but the lvalue expression *p has type int; int is not compatible with char, and int is not a character type. Therefore this program violates a requirement stated with the word "shall", which is not within a section named "constraints", and its behavior is undefined.
Note the asymmetry of the last bullet point. Any object may have its stored value accessed by an lvalue expression with character type, but an object declared to have character type may not be accessed by an lvalue expression with an incompatible type. Yes, that means the common idiom of accessing a large array of characters (such as a buffer of data read from a file) "four at a time" via a pointer to int is, strictly speaking, invalid. Many compilers make a special exception to their pointer-aliasing rules for that case, to avoid invalidating that idiom.
However, accessing a single char via a pointer to int is also invalid because (on most systems) int is bigger than char, so you read or write bytes beyond the end of the object. The standard doesn't bother distinguishing that case from the array case, but it will blow up on you regardless.
int * is a type — specifically it is pointer to int.
(type)x is a type cast. It says to reinterpret or convert x to that type. With pointer types it always means reinterpret.
i is of type char. So &i is of type char *. Casting it to int * makes it of type int * so that p can be assigned to it.
When you subsequently write via p you'll be writing a whole int.
&i gives the address of the variable i. The (int *) converts that pointer, which is of type char *, into a pointer to int.
The statement *p = 1234567892 then has undefined behaviour, since p actually points to an address of a single char, but this expression treats that location as if it contains an int (different type). In practice, the usual result is to write to memory locations past the single char, which can cause anything from data poisoning (e.g. changing values of other variables) to an immediate program crash.
Without the (int*), gcc would complain because pointer to carrots are not pointers to potatoes.
warning: initialization from incompatible pointer type [enabled by default]
Thus, this notation just means ok I know what I'm doing here, consider it a pointer to different type, ie an int.
It means that your program is about to crash with a BUS error
Surely it's typecasting. i is a character variable and p is pointer to integer.
so p= (int *) &i means p is storing the address of i which is of type char but you have type cast it, so it's fine with that. now p is point to i.
*p = 123455; // here you stored the value at &i with 123455.
when you'll print these value like
print (*p) // 123455
print (i) // some garbage -- because i is char (1 byte) and having the value of integer (4 byte). so i will take this as a decimal value and print the value accordingly.
but but let just say *p = 65;
print(*p) // 65
print(i) // A -- because char i = 65 and char 65 is 'A'
hope it'll help you.
Related
Update 2020-12-11: Thanks #"Some programmer dude" for the suggestion in the comment.
My underlying problem is that our team is implementing a dynamic type storage engine. We allocate multiple char array[PAGE_SIZE] buffers with 16-aligned to store dynamic types of data (there is no fixed struct). For efficiency reasons, we cannot perform byte encoding or allocate additional space to use memcpy.
Since the alignment has been determined (i.e., 16), the rest is to use the cast of pointer to access objects of the specified type, for example:
int main() {
// simulate our 16-aligned malloc
_Alignas(16) char buf[4096];
// store some dynamic data:
*((unsigned long *) buf) = 0xff07;
*(((double *) buf) + 2) = 1.618;
}
But our team disputes whether this operation is undefined behavior.
I have read many similar questions, such as
Why does -Wcast-align not warn about cast from char* to int* on x86?
How to cast char array to int at non-aligned position?
C undefined behavior. Strict aliasing rule, or incorrect alignment?
SEI CERT C C.S EXP36-C
But these are different from my interpretation of the C standard, I want to know if it’s my misunderstanding.
The main confusion is about the section 6.3.2.3 #7 of C11:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned 68) for the referenced type, the behavior is undefined.
68) In general, the concept ‘‘correctly aligned’’ is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which in turn is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.
Does the resulting pointer here refer to Pointer Object or Pointer Value?
In my opinion, I think the answer is the Pointer Object, but more answers seem to indicate the Pointer Value.
Interpretation A: Pointer Object
My thoughts are as follows: A pointer itself is an object. According to 6.2.5 #28, different pointer may have different representation and alignment requirements. Therefore, according to 6.3.2.3 #7, as long as two pointers have the same alignment, they can be safely converted without undefined behavior, but there is no guarantee that they can be dereferenced.
Express this idea in a program:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
if (_Alignof(char *) == _Alignof(int *)) {
// cast safely, because they have the same alignment requirement?
int *pi = (int *) pc;
printf("pi: %p\n", pi);
} else {
printf("char * and int * don't have the same alignment.\n");
}
}
Interpretation B: Pointer Value
However, if the C11 standard is talking about Pointer Value for referenced type rather than Pointer Object. The alignment check of the above code is meaningless.
Express this idea in a program:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
/*
* undefined behavior, because:
* align of char is 1
* align of int is 4
*
* and we don't know whether the `value` of pc is 4-aligned.
*/
int *pi = (int *) pc;
printf("pi: %p\n", pi);
}
Which interpretation is correct?
Interpretation B is correct. The standard is talking about a pointer to an object, not the object itself. "Resulting pointer" is referring to the result of the cast, and a cast does not produce an lvalue, so it's referring to the pointer value after the cast.
Taking the code in your example, suppose that an int must be aligned on a 4 byte boundary, i.e. it's address must be a multiple of 4. If the address of buf is 0x1001 then converting that address to int * is invalid because the pointer value is not properly aligned. If the address of buf is 0x1000 then converting it to int * is valid.
Update:
The code you added addresses the alignment issue, so it's fine in that regard. It however has a different issue: it violates strict aliasing.
The array you defined contains objects of type char. By casting the address to a different type and subsequently dereferencing the converted type type, you're accessing objects of one type as objects of another type. This is not allowed by the C standard.
Though the term "strict aliasing" is not used in the standard, the concept is described in section 6.5 paragraphs 6 and 7:
6 The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a
value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of
the lvalue becomes the effective type of the object for that access
and for subsequent accesses that do not modify the stored value. If a
value is copied into an object having no declared type using memcpy
or memmove, or is copied as an array of character type, then the
effective type of the modified object for that access and for
subsequent accesses that do not modify the value is the effective type
of the object from which the value is copied, if it has one. For all
other accesses to an object having no declared type, the effective
type of the object is simply the type of the lvalue used for the
access.
7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a
subaggregate or contained union), or
a character type.
...
87 ) Allocated objects have no declared type.
88 ) The intent of this list is to specify those circumstances in which
an object may or may not be aliased.
In your example, you're writing an unsigned long and a double on top of char objects. Neither of these types satisfies the conditions of paragraph 7.
In addition to that, the pointer arithmetic here is not valid:
*(((double *) buf) + 2) = 1.618;
As you're treating buf as an array of double when it is not. At the very least, you would need to perform the necessary arithmetic on buf directly and cast the result at the end.
So why is this a problem for a char array and not a buffer returned by malloc? Because memory returned from malloc has no effective type until you store something in it, which is what paragraph 6 and footnote 87 describe.
So from a strict point of view of the standard, what you're doing is undefined behavior. But depending on your compiler you may be able to disable strict aliasing so this will work. If you're using gcc, you'll want to pass the -fno-strict-aliasing flag
The Standard does not require that implementations consider the possibility that code will ever observe a value in a T* that is not aligned for type T. In clang, for example, when targeting platforms whose "larger" load/store instructions do not support unaligned access, converting a pointer into a type whose alignment it doesn't satisfy and then using memcpy on it may result in the compiler generating code which will fail if the pointer isn't aligned, even though memcpy itself would not otherwise impose any alignment requirements.
When targeting an ARM Cortex-M0 or Cortex-M3, for example, given:
void test1(long long *dest, long long *src)
{
memcpy(dest, src, sizeof (long long));
}
void test2(char *dest, char *src)
{
memcpy(dest, src, sizeof (long long));
}
void test3(long long *dest, long long *src)
{
*dest = *src;
}
clang will generate for both test1 and test3 code which would fail if src or dest were not aligned, but for test2 it will generate code which is bigger and slower, but which will support arbitrary alignment of the source and destination operands.
To be sure, even on clang the act of converting an unaligned pointer into a long long* won't generally cause anything weird to happen by itself, but it is the fact that such a conversion would produce UB that exempts the compiler of any responsibility to handle the unaligned-pointer case in test1.
I want to understand the real need of having a void pointer, for example in the following code, i use casting to be able to use the same ptr in different way, so why is there really a void pointer if anything can be casted?
int main()
{
int x = 0xAABBCCDD;
int * y = &x;
short * c = (short *)y;
char * d = (char*)y;
*c = 0;
printf("x is %x\n",x);//aabb0000
d +=2;
*d = 0;
printf("x is %x\n",x);//aa000000
return 0;
}
Converting any pointer type to any other pointer type is not supported by base C (that is, C without any extensions or behavior not required by the C standard). The 2018 C standard says in clause 6.3.2.3, paragraph 7:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer…
In that passage, we see two limitations:
If the pointer is not properly aligned, the conversion may fail in various ways. In your example, converting an int * to a short * is unlikely to fail since int typically has stricter alignment than short. However, the reverse conversion is not supported by base C. Say you define an array with short x[20]; or char x[20];. Then the array will be aligned as needed for a short or char, but not necessarily as needed for an int, in which case the behavior of (int *) x would not be defined by the C standard.
The value that results from the conversion mostly unspecified. This passage only guarantees that converting it back yields the original pointer (or something equivalent). It does not guarantee you can do anything useful with the pointer without converting it back—you cannot necessarily use a pointer converted from int * to access a short.
The standard does make some additional guarantees about certain pointer conversions. One of them is in the continuation of the passage above:
… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
So you can use a pointer converted from int * to access the individual bytes that represent an int, and you can do the same to access the bytes of any other object type. But that guarantee is made only for access the individual bytes with a character type, not with a short type.
From the above, we know that after the short * c = (short *)y; in your example, y does not necessarily point to any part of the x it originated from—the value resulting from the pointer conversion is not guaranteed to work as a short * at all. But, even if it does point to the place where x is, base C does not support using c to access those bytes, because 6.5 7 says:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
So the *c = 0; in your example is not supported by C for two reasons: c does not necessarily point to any part of x or to any valid address, and, even if it does, the behavior of modifying part of the int x using short type is not defined by the C standard. It might appear to work in your C implementation, and it might even be supported by your C implementation, but it is not strictly conforming C code.
The C standard provides the void * type for use when a specific type is inadequate. 6.3.2.3 1 makes a similar guarantee for pointers to void as it does for pointers to objects:
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
void * is used with routines that must work with arbitrary object types, such as qsort. char * could serve this purpose, but it is better to have a separate type that clearly denotes no specific type is associated with it. For example, if the parameter to a function were char *p, the function could inadvertently use *p and get a character that it does not want. If the parameter is void *p, then the function must convert the pointer to a specific type before using it to access an object. Thus having a special type for “generic pointers” can help avoid errors as well as indicate intent to people reading the code.
Why void pointer if pointers can be casted into any type(in c)?
C does not specify that void* can be cast into a pointer of any type. A void * may be cast into a pointer to any object type. IOWs, a void * may be insufficient to completely store a function pointer.
need of having a void pointer
A void * is a universal pointer for object types. Setting aside pointers to const, volatile, etc. concerns, functions like malloc(), memset() provide universal ways to allocate and move/set data.
In more novel architectures, a int * and void * and others have different sizes and interpretations. void* is the common pointer type for objects, complete enough to store information to re-constitute the original pointer, regardless of object type pointed to.
I thought I understood how C arrays, pointers and pointers to arrays worked, but now I stumbled upon a piece of working code which I don't understand:
int sum(const int * const buf, int len)
{
int result = 0;
const int (*p)[];
int n;
p = (const int(*)[]) buf;
// p = buf without cast gives compiler warning here (but works)
// p = (const int(*)[]) &buf; // Doesn't work! Segfault!
for( n=0; n<len; n++)
{
result += (*p)[n];
}
return result;
}
This actually works. But how? The question is: how does that assignment from "buf" to "p" work? I thought this declaration of p was similar to "pointer to pointer to int", so I would have expected a "&" necessesary at the assignment. Why is this not necessary?
In fact the following prototype also works (with the same function body above):
int sum(const int buf[const], int len)
Now it seems even more clear that the declaration of "p" adds one level of pointer re-direction compared to the declaration of "buf". Still ... the assignment works fine without any "&" necessary. Can someone explain it?
First of all, please note that this code is not good practice. The pointer conversion is fishy-looking and unusual, and in particular it casts away const qualifiers, which is bad practice. (Declaring a parameter as ...* const buf is pretty fishy to begin with though.)
This actually works. But how?
There is a pointer conversion from (qualified) int pointer to int array pointer. Given that these two pointer types do not have different representation or different alignment (highly unlikely but theoretically possible), the pointer conversion in itself is ok (as per C11 6.3.2.3/8).
What matters then is the effective type of the data, which is an (array of) int. Effective type is a formal C term used for determining what type that is actually stored at a location, no matter the pointer types used for accessing. As long as the data is accessed through a pointer type that is compatible with the effective type of what's stored there, the code should work fine.
The formal reason why this is fine is C11 6.5/7 ("the strict aliasing rule"):
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
/--/
— an aggregate or union type that includes one of the aforementioned types among its
members
Where "aggregate" is C standard gibberish for arrays and structs. If we access the data through an expression of array type, where the array element type is a type/qualified type compatible with the actual type, int in this case, everything is fine.
As for how it works, it simply de-references an array pointer. p = (const int(*)[]) buf; says "threat what's stored in this variable as a pointer to array of integer (and qualifiers be damned)". Then (*p)[n] takes the array pointer, de-references it to get the actual array, then uses the array index.
p = buf without cast gives compiler warning here
If you compile with gcc -pedantic-errors you get a more correct error. The pointer types are not compatible and so the compiler must generate a diagnostic message here - since p = buf is not valid C.
p = (const int(*)[]) &buf; // Doesn't work! Segfault!
This is because what's stored at &buf is not an array of int, it is just the pointer buf itself, likely allocated on the stack as a parameter to your function.
So to clear out misunderstandings from the title (not sure how to ask the question in the title) I want to read from a file(char array), pass it as an void* so i can read undependable of datatype by incrementing the pointer. So here's an simple example of what I want to do in C code:
char input[] = "D\0\0Ckjh\0";
char* pointer = &input[0]; //lets say 0x00000010
char type1 = *pointer; //should be 'D'
pointer += sizeof(char); //0x00000020
uint16_t value1 = *(uint16_t*)pointer; //should be 0
pointer += sizeof(uint16_t); //0x00000040
char type2 = *pointer; //should be 'C'
pointer += sizeof(char); //0x00000050
uint32_t value2 = *(uint32_t*)pointer; //should be 1802135552
This is just for educational purpose, so I would just like to know if it is possible or if there is a way to achieve the same goal or something alike. Also the speed of this would be nice to know. Would it be faster to just keep the array and just make bitshifting on the chars as you read them or is this actually faster?
Edit: edit on the c code and changed void* to char*;
This is wrong in two ways:
void is an incomplete type that cannot be completed. An incomplete type is a type without a known size. In order to do pointer arithmetics, the size must be known. The same is true for dereferencing a pointer. Some compilers attribute the size of a char to void, but that's an extension you should never rely on. Incrementing a pointer to void is wrong and can't work.
What you have is an array of char. Accessing this array through a pointer of a different type violates strict aliasing, you're not allowed to do that.
That's actually not what your current code does -- looking at this line:
uint32_t value2 = (int)*pointer; //should be 1802135552
You're just converting the single byte (assuming your pointer points to char, see my first point) to an uint32_t. What you probably meant is
uint32_t value2 = *(uint32_t *)pointer; //should be 1802135552
which might do what you expect, but is technically undefined behavior.
The relevant reference for this second point is e.g. in §6.5 p7 in N1570, the latest draft for C11:
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the
object,
— a type that is the signed or unsigned type corresponding to a qualified version of the
effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
The reasoning for this very strict rule is for example that it enables compilers to do optimizations based on the assumption that two pointers of different types (except char *) can never alias. Other reasons include alignment restrictions on some platforms.
Even if you fix your code to cast pointer to correct type (like int *) before dereferencing it, you might have problems with alignment. For example on some architectures you simply can not read an 4-byte int if it is not aligned to 4-byte word boundary.
A solution which would definitely work is to use something like this:
int result;
memcpy(&result, pointer, sizeof(result));
UPDATE:
in the updated code in the question
uint16_t value1 = *(uint16_t*)pointer;
exactly violates strict aliasing. It's invalid code.
For more details, read the rest of the answer.
Initial version:
Technically, you are not allowed to dereference a void pointer in first place.
Quoting C11, chapter §6.5.3.2
[...] If the operand points to a function, the result is
a function designator; if it points to an object, the result is an lvalue designating the
object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. [...]
but, a void is a forever-incomplete type, so the storage size is not known, hence the dereference is not possible.
A gcc extension allows you to dereference the void pointer and perform arithmatic operation on them, considering it as alias for a char pointer, but better, do not reply on this. Please cast the pointer to either a character type or the actual type (or compatible) and then, go ahead with dereference.
That said, if you cast the pointer itself to some other type than a character type or an incompatible type with the original pointer, you'll violate strict aliasing rule.
As mentioned in chapter §6.5,
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the
object,
— a type that is the signed or unsigned type corresponding to a qualified version of the
effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
and, chapter §6.3.2.3
[....] When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
Take a look at the following program. What I don't understand is why do I have to cast the address of the variable x to char* when it actually would be absolutely useless if you think about it for a second. All I really need is only the address of the variable and all the necessary type information is already in place provided by the declaration statement char* ptr.
#include <stdio.h>
int main(void) {
int x = 0x01020309;
char* ptr = &x; /* The GCC compiler is going to complain here. It will
say the following: "warning: initialization from
incompatible pointer type [enabled by default]". I
need to use the cast operator (char*) to make the
compiler happy. But why? */
/* char* ptr = (char*) &x; */ /* this will make the compiler happy */
printf("%d\n", *ptr); /* Will print 9 on a little-endian machine */
return 0;
}
The C Standard, 6.2.5 Types, paragraph 28 states:
A pointer to void shall have the same representation and
alignment requirements as a pointer to a character type.
Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and
alignment requirements. All pointers to structure types shall have
the same representation and alignment requirements as each other.
All pointers to union types shall have the same
representation and alignment requirements as each other.
Pointers to other types need not have the same representation or alignment requirements.
Since different types of pointers can have differing implementations or constraints, you can't assume it's safe to convert from one type to another.
For example:
char a;
int *p = &a
If the implementation has an alignment restriction on int, but not on char, that would result in a program that could fail to run.
This is because pointers of different types point to blocks of memory of different sizes even if they point to the same location.
&x is of type int* which tells the compiler the number of bytes (depending on sizeof(int)) to read when getting data.
Printing *(&x) will return the original value you entered for x
Now if you just do char* ptr = &x; the compiler assigns the address in &x to your new pointer (it can as they are both pointers) but it warns you that you are changing the size of the block of memory being addressed as a char is only 1 byte. If you cast it you are telling the compiler that this is what you intend.
Printing *(ptr) will return only the first byte of the value of x.
You are correct that it makes no practical difference. The warning is there to inform you that there might be something fishy with that assignment.
C has fairly strong type-checking, so most compilers will issue a warning when the types are not compatible.
You can get rid of the warning by adding an explicit cast (char*), which is you saying:
I know what I'm doing, I want to assign this value to my char* pointer even if the types don't match.
Its just simple as you assign integer type to character. similarly you are trying to assign integer type pointer to character type pointer.
Now why is so because this is how c works, if you increment a character pointer it will give you one byte next address and incrementing integer pointer will give you 2 byte next address.
According to your code, x is of type int. So the pointer that points to x should be of type int *. Compiler gives such error because you use a pointer which is not int *.
So make your pointer either int *, or void * then you don't need cast.