I'm new to C and came across some code dealing with pointers and could use some help deciphering it. What does this mean? It appears to be casting variable p to be a pointer to an int. I'm confused by the (int *) code.
*(int *)p = 0x113
It casts p to a pointer-to-int, and then stores the (integer) value 0x113 at the referenced location.
When you run across something like this, the best way to approach it is to separate it an take it one step at a time. Start with what is immediately casting the variable at issue and go from there. Here is it
* (int *) p
Starting at the middle, you have a cast to a pointer of type int:
(int *)
The cast is operating on variable p. So you know you are casting p to int *. So the following just says cast variable p to type int *:
(int *)p
Then the last piece of the puzzle is the * which says dereference the whole 'shebang'. So in its final form, you are simply dereferencing p which has been cast to int *:
*(int *)p
It says "treat p as a pointer to type int, then store value of 0x113 in the 4 bytes pointed to by p" (assuming int is 4 bytes).
The effect is that if, for example, p is declared as a pointer to char, this command will be able to modify 4 bytes instead of just one (size of char).
Related
I somehow understand the idea about casting, but still have the following questions about it, when we cast a variable from one type to another:
Does casting change the actual data type generally? Like eg. if we have char *name ="james" and we cast the char pointer ==> int *name = (int *) name
Do the types of all fields (members) also change in case of a structure data type in C? I.e. if we have a struct student {int id, char *name} and there is a pointer to an instance of struct student and type cast it to another type, do the fields also change?
From another answer here,
casting (also called type coercion) does not change the data - the
underlying bits - but it changes the type, i.e., how those bits are
interpreted.
In other words, a cast tells the compiler:
"You would think the expression has this type, but I say you to use the expression as it had this different type"
At this point the compiler says "Ok" and compile code to do what you ask for with the cast. Take this example (probably there are better ones):
int a,b;
int *p;
a=256; // does not fit in a single byte
p=&a; // a pointer to a
b=*p; // <- now b is 256, the value of a
b=*(char *) p; // <- now b is 0 (probably)
The two assignments to "b" are different. In the first case the compiler nows that p points to integers, so it will move in b 4 or 8 bytes taken from a.
The second assignment, with the cast, tells the compiler to act as p was not a pointer to an integer, but a pointer to a char. And the compiler obeys, and takes only one byte from the value of a (pointed to by p).
Does casting change the actual data type generally? Like eg. if we have char *name ="james" and we cast the char pointer ==> int *name = (int *) name
Casting a pointer does not change the memory the pointer points to.
When a pointer, say p, is dereferenced, as with *p, and used to get a value from memory, the type of the pointer determines how the compiler interprets the memory. If p is a char *, then using *p for its value loads one byte from memory and interprets it as a char value. If p is an int *, then using *p for its value loads as many bytes from memory as an int uses and interprets them as an int value. If p is an int * and we use * (char *) p for its value, then (char *) p is a char *, so * (char *) p loads one byte from memory and interprets it as a char value.
Conversely, when using *p to store a value to memory, the value will be encoded according to the rules for the *p type, and the resulting bytes will be written to memory.
The C standard has rules about which types may be used to access memory that has been established to contain data of another type. If those rules are not followed, the behavior of the program is not defined by the C standard. Using an int * converted from a pointer to a char in an array of char is not one of the defined uses. Nominally, it asks the compiler to interpret the bytes of the array as if they encoded an int value, but, because the code is not following the rules of the C standard, the compiler might or might not do that.
Do the types of all fields (members) also change in case of a structure data type in C? I.e. if we have a struct student {int id, char *name} and there is a pointer to an instance of struct student and type cast it to another type, do the fields also change?
Casting a pointer does not change the memory the pointer points to. If you cast a pointer to one structure type to a pointer to another structure type and attempt to access memory with it, it might not work as you desire.
I cannot understand why are pointer type casts necessary, as long as pointers point to an address and their type is important only when it comes to pointer arithmetic.
That is to say, if I encounter the next code snippet:
int a = 5; then both
char*b = (char*)&a; and int*c = (int*)&a
point to the very same memory location.
Indeed, when executing char*b = (char*)&a part of the memory contents may be lost, but this is due to the type of b is char* which can store only sizeof(char) bytes of memory, and this could be done implicitly.
The pointer type is important when you dereference it, since it indicates how many bytes should be read or stored.
It's also important when you perform pointer arithmetic, since this is done in units of the size that the pointer points to.
The type of the pointer is most important when you dereference the pointer.
The excerpt
char *b = (int*)&a;
is wrong because int * cannot be assigned to char *. You meant char *b = (char *)&a;. The C standard says you need an explicit cast because the types are not compatible. This is the "yeah yeah, I know what I am doing".
The excerpt
int*c = (int*)&a;
is right, but &a is already a pointer to an int, so the cast will do no conversion, therefore you can write it as int *c = &a;.
as long as pointers point to an address and their type is important only when it comes to pointer arithmetic.
No, not only for pointer arithmetic. Its also important for accessing the data pointed by the pointer, the wrong way of accessing as in your example leads to improper results.
when executing charb = (int)&a part of the memory contents may be lost, but this is due to the type of b is char* which can store only sizeof(char) bytes of memory,
First of all, that is wrong to do (unless we really need to extract 1-byte out of 4-bytes)
Second, the application of pointer cast is mostly used w.r.t void*(void pointers) when passing data for a function which can handle many different type, best example is qsort
I am trying to understand the difference between these two things in C to understand conceptually the differences.
Here is example:
int y = *(int *)x;
vs.
int * y = (int*)x;
The second option is casting x to a int pointer, but not doing anything with the pointer yet (aside from storing it). At some point in the future, that pointer could be dereferenced to produce an int (assuming aliasing rules aren't being broken, the original pointer is valid, etc.) or to store an int (by assigning to the dereferenced pointer, e.g. *y = 5;).
The first option is the second option, followed immediately by dereferencing the resulting pointer to get the value it points to. The new pointer isn't saved off, it's just used to load the int it points to, then the pointer is discarded and only the int is stored.
If I have a pointer to an array of char*s, in other words, a char** named p1, what would I get if I do (char*)p1? I’m guessing there would be some loss of precision. What information would I lose, and what would p1 now be pointing to? Thanks!
If you had asked about converting an int ** to an int *, the answer would be different. Let’s consider that first, because I suspect it is more representative of the question you intended to ask, and the char * case is more complicated because there is a special purpose involved in that.
Suppose you have several int: int a, b, c, d;. You can make an array of pointers to them: int *p[] = { &a, &b, &c, &d };. You can also make a pointer to one of these pointers: int **q = &p[1];. Now q points to p[1], which contains the address of b.
When you write *q, the compiler knows q points to a pointer to an int, so it knows *q points to an int. If you write **q, the compiler, knowing that *q points to an int, will get *q from memory and use that as an address to get an int.
What happens if you convert q to an int * and try to use it, as in printf("%d\n", * (int *) q);? When you convert q to an int * you are (falsely) telling the compiler to treat it as a pointer to an int. Then, * (int *) q tells the compiler to go to that address and get an int.
This is invalid code—its behavior is not defined by the C standard. Specifically, it violates C 2018 6.5 7, which says that an object shall be accessed only by an lvalue expression that has a correct type—either a type compatible with that of the actual object or certain other cases, none of which apply here. At the place q points, there is a pointer to an int, but you tried to access it as if it were an int, and that is not allowed.
Now let’s consider the char ** to char * case. As before, you might take some char **q and convert it to char *. Now, you are telling the compiler to go to the place q points, where there is a pointer to a char, and to access that memory location as if there were a char there.
C has special rules for this case. You are allowed to examine the bytes that make up objects by accessing them through a char *. So, if you convert a char ** to char * and use it, as in * (char *) q, the result will be the first (lowest addressed) byte that makes up the pointer there. You can even look at the rest of the bytes, using code like this:
char *t = (char *) q;
printf("%d\n", t[0]);
printf("%d\n", t[1]);
printf("%d\n", t[2]);
printf("%d\n", t[3]);
This code will show you the decimal values for the first four bytes that make up the pointer to char that is at the location specified by q.
In summary, converting a char ** to char * will allow you to examine the bytes that represent a char *. However, in general, you should not convert pointers of one indirection level to pointers of another indirection level.
I find pointers make much more sense when I think of them as memory addresses rather than some abstract high level thing.
So a char** is a memory address which points to, a memory address which points to, a character.
0x0020 -> 0x0010 -> 0x0041 'A'
When you cast you change the interpretation of the data, not the actual data. So the char* is
0x0020 -> 0x0010 (unprintable)
This is almost certainly not useful. Interpreting this random data as a null terminated string would be potentially disastrous.
I was looking at an answer to "Pointers in C: when to use the ampersand and the asterisk" and am confused about the example int *p2 = &i; (from Dan Olson's answer)
The address of i is not an int, it's something like 0x02304 say, right? So how can we put this into p2? How can a C pointer of type int hold a memory address, given that a byte memory address is not of type int?
Thanks!
P.S. For anyone confused on this point, another thread I found helpful (though it didn't answer this question for me) is "What exactly is a C pointer if not a memory address" Good luck.
I think the reason int *p2 = &i; is confusing is the combined effect of the simultaneous declaration and instantiation, and the spacing. I'll explain.
Typically, for a pointer-to-integer p2 and an integer i, writing "*p2" dereferences p2 and gives the int living at the address &i.
So the code "int *p2 = &i;" makes it look like an int is being set equal to a memory address.
Indeed, the code
int i = 1;
int *p2;
*p2 = &i
is wrong, because in the last line, *p2 is an int since it is p2 dereferenced, and &i is a pointer.
Why int *p2 = &i; is NOT doing the same thing as the last line of the flawed code above:
The code int *p2 = &i; is different from the 3 lines of code above because it is a declaration. When you declare a pointer variable, you put the type, (e.g. int, long, char, etc.), and the name of your variable (like normal) - and you also put an asterisk * in between these two pieces (see #chqrlie answer above with regard to the many spacing options for this - takehome is best-practice is to adhere the * to the variable name). In a declaration, the * is NOT dereferencing the pointer. Rather, it is telling the compiler that the variable myPointer will be a pointer to memory holding data of the declared type (the int, long, char, etc. from before). So, in the bit of code "int *p2 = &i;", where the pointer is being declared and instantiated at the same time, the * does not render an int on the left-hand side. For declarations, while it is safer (as #chqrlie points out) to put the * next to the variable name, this does not unwrap the pointer even if the pointer is instantiated in the declaration. For declarations, think of the * as being attached to the type (rather than to the variable name, where for good reasons it is likely to be). I like to declare pointers with the * right next to the pointer name, but just understand that when instantiating a pointer in the declaration, a perhaps clearer way for a new learner to imagine the line would be int* p2 = &1:
int* (type pionter-to-int) p2 (name of pointer) = &i (equals the pointer that gives the address of integer i) ;
Thank you to everyone who answered and commented, and good luck to everyone who may have come here trying to figure out something about pointers.
The address of i is not an int, it's something like 0x02304 say, right?
Pointers are variables that hold memory addresses. An address, just like the address of your house, is an integer assigned to the location of a byte of memory.
How can a C pointer of type int hold a memory address
In your example, p2 is variable of type pointer-to-int. It is a pointer - a memory address - that you're declaring points to memory where an int variable will be stored.
p2 is not an int, it is a pointer to int. As such, it can hold the address of an int variable.
The syntax int *p2; defines a pointer to int.
Initializing p2 with &i stores the address of variable i into p2. Modifying the value pointed to by p2 will modify the value of i.
The following alternative syntaxes are all equivalent:
int *p2 = &i;
int * p2 = & i;
int * p2 =& i;
int*p2=&i;
int* p2 = &i;
The preferred syntax is int *p2 = &i; because it avoids a common misunderstanding when defining multiple variables on the same line:
int *p1, *p2; // defines 2 pointers to int
int *p1, p2; // p1 is a pointer-to-int, whereas p2 is an int
Tacking the * to the type makes the latter definition very confusing:
int* p1, p2; // p1 is a pointer-to-int, whereas p2 is an int
As a consequence, defining variables with different indirection levels on the same line is also strongly discouraged.
Pointers are abstractions of memory addresses, with some associated type semantics.
p2 has type int *, or "pointer to int". It stores the location of an integer object (in this case, the location of the object i). The type of the expression *p2 is int:
p2 == &i; // both expressions have type int * and evaluate to an address value
*p2 == i; // both expressions have type int and evaluate to an integer value
Pointers are as big as they need to be to store an address value, however that address value is represented for the given platform (whether that's a single integer value, or a pair of values representing a page number and offset, or some other format). Note that different pointer types may be different sizes, although on modern desktop architectures they are all the same size (32 or 64 bit).
The type of the pointer matters for pointer arithmetic. Given a pointer p, the expression p + 1 yields the address of the next object of the pointed-to type. If the pointed-to type is 1 byte wide (such as char) and its current address is 0x8000, then p + 1 yields the address of the next byte, or 0x8001. If the type is 4 bytes wide (such as a long) and its current address is 0x8000, then p + 1 yields the address of the fourth next byte, or 0x8004.
A pointer is its own type.
If it were used with a different syntax it could be written like this:
Pointer x = new Pointer(int, address);