I'm studying casting in C++ and the code after is magic to me.
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { }
};
#define SOME_VALUE 8
int main() {
cout << SOME_VALUE <<endl;
getchar();
}
the output is: 8
The code is very simple, but What type of SOME_VALUE? int, or double or char?
After is more complex:
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { }
};
#define SOME_VALUE 8
int main() {
cout << (Base*)SOME_VALUE-SOME_VALUE <<endl;
getchar();
}
The output is: FFFFFFE8
Following this code, I can understand that SOME_VALUE is numeric type. I also test sizeof(SOME_VALUE) and the out put is 4. But if SOME_WHAT is numeric, how can it change to object Pointer? And how Object Pointer can minus to integer?
#define is a preprocessor command. It gets evaluated before the code gets compiled. All that happens is that SOME_VALUE in the main function has it's text replaced by the text SOME_VALUE is defined as. That is 8.
SOME_VALUE itself doesn't have a C++ type because it only exists before preprocessing. After preprocessing SOME_VALUE won't exist in the C++ program, you'll just have a literal value 8 which is an int.
For the second question, the cast to Base* uses a C style cast. That is capable of converting anything to anything just by treating the raw memory of what you're converting as being of the target type. So, it can be quite dangerous if the memory being cast doesn't match the target type. For C++, I suggest using static_cast or reinterpret_cast to make this more explicit as to what's being casted.
I think (Base*)SOME_VALUE will end up as a Base* to the memory address 8. So, this is a pointer to a Base object that starts on the 8th byte in memory. There probably isn't a Base object at location 8 in memory, so it's not actually very useful. Then "- 8" takes away 8 multiples of the size of the Base* type. On a 32bit computer, pointers are 32bits, or 4bytes. So, 8 - (4*8) = -24 decimal, which is FFFFFFE8 in hex.
If you want to know why a computer represents negative numbers as big numbers, that's a different question. Start here: http://en.wikipedia.org/wiki/Signed_number_representations
SOME_VALUE is a macro--it doesn't have a type. 8, however, is an integer.
Use #define SOME_VALUE ((Base*)8), if you want SOME_VALUE to always act like a Base*.
cout << (Base*)SOME_VALUE-SOME_VALUE <<endl;
Is basically a (horrible) way of doing:
Base* b = 8;
b = b - 8;
The 8 will be silently multiplied by the size of Base though (so you're subtracting 8 base slots, not 8).
Pointers are typically unsigned, so what's happening is that the unsigned pointer is wrapping around.
0xFFFFFFE8 is 4294967272 or (assuming 4 byte unsigned int with usual wrap around) 8 - 24.
Also, you should never do this in real code. Assigning an arbitrary value to a pointer is sure to end in a fiery explosion.
An easier to understand situation might be like this:
int* p = (int*) 24;
p -= 4; //like ((char*) p) - 4 * sizeof(int)
With 4 byte integers, the value of p would then be 8 because 24 - 4 * sizeof(int) = 24 - 4 * 4 = 24 - 16 = 8.
Related
I have a question about pointers, and memory addresses:
Supposing I have the following code:
int * array = (int *) malloc(sizeof(int) * 4);
Now in array im storing a memory address, I know that c++ takes already care when adding +1 to this pointer it will add 4 bytes, but what If I want to add manually 4 bytes?
array + 0x004
If im correct this will lead to add 4*4 (16) bytes, but my Idea is to add manually those 4 bytes.
Why? Just playing around, i've tried this and I got a totally different result from what I expected, then i've researched and i've seen that c++ takes already care when you add +1 to a pointer (it sums 4 bytes in this case).
Any idea?
For a pointer p to a type T with value v, the expression p+n will (on most systems anyway) result in a pointer to the address v+n*sizeof(T). To get a fixed-byte offset to the pointer, you can first cast it to a character pointer, like this:
reinterpret_cast<T*>(reinterpret_cast<char*>(p) + n)
In c++, sizeof(char) is defined to be equal to 1.
Do note that accessing improperly aligned values can have large performance penalties.
Another thing to note is that, in general, casting pointers to different types is not allowed (called the strict aliasing rule), but an exception is explicitly made for casting any pointer type to char* and back.
The trick is convert the type of array into any pointer-type with a size of 1 Byte, or store the pointer value in an integer.
#include <stdint.h>
int* increment_1(int* ptr) {
//C-Style
return (int*)(((char*)ptr) + 4);
}
int* increment_2(int* ptr) {
//C++-Style
char* result = reinterpret_cast<char*>(ptr);
result += 4;
return reinterpret_cast<int*>(result);
}
int* increment_3(int* ptr) {
//Store in integer
intptr_t result = reinterpret_cast<intptr_t>(ptr);
result += 4;
return reinterpret_cast<int*>(result);
}
Consider that if you add an arbitrary number of bytes to an address of an object of type T, it no longer makes sense to use a pointer of type T, since there might not be an object of type T at the incremented memory address.
If you want to access a particular byte of an object, you can do so using a pointer to a char, unsigned char or std::byte. Such objects are the size of a byte, so incrementing behaves just as you would like. Furthermore, while rules of C++ disallow accessing objects using incompatible pointers, these three types are excempt of that rule and are allowed to access objects of any type.
So, given
int * array = ....
You can access the byte at index 4 like this:
auto ptr = reinterpret_cast<unsigned char*>(array);
auto byte_at_index_4 = ptr + 4;
array + 0x004
If im correct this will lead to add 4*4 (16) bytes
Assuming sizeof(int) happens to be 4, then yes. But size of int is not guaranteed to be 4.
When I want to calculate the address of a function, I do the following:
HMODULE base = GetModuleHandle(L"program.exe"); // Module base addr
// Adding an offset to the base
std::cout << (base + 0x8F0A0) << std::endl; -> Wrong!
I'm not sure why the result is wrong. I've tested it via online hex calcs and also have debugger to check both values.
Could base be considered decimal and other being hex, produce wrong results?
How can I get a result in hex?
As explained here, depending on whether STRICT is defined, HMODULE is essentially either a void* or a <unique type>*, the purpose of this being to make each handle type a different C++ type, meaning compiler errors when you mix and match. In the former case, pointer arithmetic won't compile. In the latter case, it will compile, but you can't rely on anything happening because pointer arithmetic takes the type's size into account and because pointer arithmetic is undefined if you leave the object/array being pointed to.
You should treat this pointer as pointing to nothing in particular, and therefore not do pointer arithmetic. You have to reinterpret_cast it to an integral type that you're sure is large enough (std::uintptr_t) and then do arithmetic on that integral value.
In my local header, this unique type contains an int member, so adding 1 will actually move the pointer ahead by 4 bytes (you know, except for the undefined behaviour and all). It just so happens that 0x00DE0000 + 4 * 0x8F0A0 is your 0x0101C280 value.
You're problem lies with the value GetModuleHandle(L"program.exe") returning: 00DE0000. You need to utilise C hexadecimal syntax, so you need to add and precede "0x" to your hex number 00DE0000.
Hence, your base number should be casted to a numeric value: 0x00DE0000
0x00DE0000 is equal to 00DE0000
Try using std::string to_string(int value); to convert it to string, then convert your hex values (base) to C hexadecimal syntax (add "0x" at the beginning of your hex value). To finish off, convert your base value back to a numeric value (e.g. use std::stoi) and perform the addition using std::hex.
Try this code here.
#include <iostream>
int main () {
int hex1 = 0x8F0A0;
int hex2 = 0x00DE0000; // Using int values
std::cout << std::hex << hex1 + hex2 << std::endl;
}
As Chris has said, I had the same case, solving the thing like this:
int offset = 0x8F0A0;
std::uintptr_t base = reinterpret_cast<uintptr_t>(GetModuleHandle(L"program.exe"));
// Here added 4 bytes to the offset.
std::cout << std::hex << (base + (offset + 4096)) << std::endl;
I am learning C and recently had my class on pointers and memory addresses.
The teacher told us that memory locations are basically unsigned numbers, so we can display them using the following code:
int a;
printf("%u", &a);
or
int a, *p=&a;
printf("%u", p);
Indeed, this works, but I also read in some forums that one should use %p or %x for printing addresses. So, they must be hex numbers... Are the integers I am seeing above actually hexadecimal numbers converted to decimal? And what are addresses actually in their basic form - hex or integer or simply binary nos.
Please help me on this.
Addresses in their basic form are simply values. Hex, or binary, or octal are representations of a number. For example, 42, 0x2a, 2a16, 052, 528, 1010102 and 1042 are all different representations of the same value.
In terms of what you should be using as a format string, %p is the correct one. There is no guarantee that unsigned integers will have the same number of bits as a pointer so you may well lose information.
In fact, providing and argument to printf that doesn't match the corresponding format specifier is actually undefined behaviour.
Using %u or %x variants may well work on most systems where pointers and unsigned integers are of a compatible size but truly portable code wouldn't rely on that. For example, an implementation is free to have a 16-bit unsigned integer (to satisfy the minimum range requirements in ISO C11 Appendix E) and a 1024-bit pointer type (I gotta get me one of those machines).
Here’s the standard way of doing it in C:
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int x = 0;
printf( "The value of &x is %" PRIxPTR " in hex and %" PRIuPTR
" in decimal, and %%p displays %p.\n",
(uintptr_t)&x,
(uintptr_t)&x,
(void*)&x);
return EXIT_SUCCESS;
}
A uintptr_t is an integer that’s able to store a pointer to an object safely.
And the C++ solution:
#include <climits>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iostream>
using std::cout;
using std::endl;
using std::resetiosflags;
using std::setfill;
using std::setiosflags;
using std::setw;
using std::streamsize;
int main(void) {
int x = 0;
static const streamsize hex_width = static_cast<streamsize>(sizeof(int*)*CHAR_BIT/4);
static const streamsize dec_width = 0; // Don't pad.
cout << "The value of &x is " << resetiosflags(cout.basefield) << setw(hex_width) << setfill('0')
<< setiosflags(cout.hex) << reinterpret_cast<uintptr_t>(&x) << " in hex, "
<< resetiosflags(cout.basefield) << setw(dec_width)
<< setiosflags(cout.dec) << reinterpret_cast<uintptr_t>(&x) << " in decimal, and "
<< resetiosflags(cout.basefield) << static_cast<void*>(&x) << " as a pointer." << endl;
return EXIT_SUCCESS;
Learning C, one of the hurdles everyone has to get over is making friends with pointers. In C, unlike other languages, you are coding at a very low-level of the machine and that just about everything you do in C is a memory operation. The key to learning C is knowing "What is at that memory address?", "How big of a block of memory do I have?" and "How do I work with it correctly?". Unfortunately, many times people make that process much harder than it needs to be.
One thing that really helps, and that is fundamental to understanding a bulk of low-level programming, is simply that all variables are nothing more than a label, or better yet, an alias for a memory address.
The only thing that differentiates a normal variable and a pointer variable is that instead of being an alias to where some direct or immediate value is stored in memory (like 5 or 21), a pointer is an alias to a memory location where the address of something else is stored. Simply put, a pointer stores a memory address as its value.
Think of the basics this way:
int x = 5;
What is x? It's a label for a memory address. What memory address does x label (alias)? &x. What's stored at that address? 5. Good so far?
Well, what are pointers?
int *y; /* what's so special about the '*' in the declaration?
Nothing, just basic syntax for pointer declaration. */
y = &x; /* which stores the address of x as the value of y.
And what is x? -- a label to a memory location */
What is y? It's a label for a memory address. What memory address does y label (alias)? &y. What's stored at that address? &x (the address of x). Still good?
Then let's look at a short example to hopefully help cement the basics, so you can move on to truly using pointers as intended, to access and manipulate blocks of memory such as arrays, linked-lists, stacks, etc...:
#include <stdio.h>
void prn_values (int val, int *ptr);
int main (void) {
int x = 5; /* declare variable & pointer */
int *y = &x; /* y now holds the address of x */
prn_values (x, y);
int z = 7;
y = &z; /* y now holds the address of z */
prn_values (z, y);
z = x; /* y still holds the value of z, */
prn_values (z, y); /* but the value of z has changed */
*y = 9; /* y still holds z, but we have changed z by */
/* changing the value at the address stored in y */
printf ("\n z : %-14d (value)\n &z : %-14p (address)\n", z, &z);
/* actually using a pointer to an array */
char array[] = "pointer arithmetic.";
char *p = array; /* p points to the start address of array */
printf ("\n array : %p (address)\n array [0]: %p (address)\n\n",
array, &array[0]);
while (*p) {
printf (" %c : %p\n", *p, p);
p++;
}
return 0;
}
void prn_values (int val, int *ptr)
{
printf ("\n x : %-14d (value)\n &x : %-14p (address)\n", val, &val);
printf (" y : %-14p (value)\n &y : %-14p (address)\n *y : %-14d "
"(dereference)\n", ptr, &ptr, *ptr);
}
Compile
gcc -Wall -Wextra -o pointers pointers.c
Output
Below, the integer pointer y is assigned the value of x, then z and the values, addresses and value at the address of the pointer are output. Note how the value of the pointer is the address of the variable it points to. While y points to z, the value of z is changed. Since y holds (points to) the memory address aliased by z, *y reflects the change as well. Then note how the change works both ways. If the value at the address held by y is changed by assignment to *y, the change is reflected in z as well.
That is all rudimentary pointer basics, that does very little to show the true power and use of pointers. It is pointer arithmetic and the ability to pass a pointer as a function argument where the use and value of pointers is seen. Note how the final while loop, uses the pointer p to the character array to access each and every address (holding a character) in the array by nothing more than incrementing p by 1 (p++;) during each iteration.
$ ./pointers
x : 5 (value)
&x : 0x7ffff2c72590 (address)
y : 0x7ffff2c72590 (value)
&y : 0x7ffff2c725b0 (address)
*y : 5 (dereference)
z : 7 (value)
&z : 0x7ffff2c725a0 (address)
y : 0x7ffff2c725a0 (value)
&y : 0x7ffff2c725b0 (address)
*y : 7 (dereference)
z : 5 (value)
&z : 0x7ffff2c725a0 (address)
y : 0x7ffff2c725a0 (value)
&y : 0x7ffff2c725b0 (address)
*y : 5 (dereference)
z : 9 (value)
z : 0x7ffff2c725a0 (address)
array : 0x7ffff2c725c0 (address)
array [0]: 0x7ffff2c725c0 (address)
p : 0x7ffff2c725c0
o : 0x7ffff2c725c1
i : 0x7ffff2c725c2
n : 0x7ffff2c725c3
t : 0x7ffff2c725c4
e : 0x7ffff2c725c5
r : 0x7ffff2c725c6
: 0x7ffff2c725c7
a : 0x7ffff2c725c8
r : 0x7ffff2c725c9
i : 0x7ffff2c725ca
t : 0x7ffff2c725cb
h : 0x7ffff2c725cc
m : 0x7ffff2c725cd
e : 0x7ffff2c725ce
t : 0x7ffff2c725cf
i : 0x7ffff2c725d0
c : 0x7ffff2c725d1
. : 0x7ffff2c725d2
Pointer arithmetic works for all array types (char, int, struct foo, whatever). By declaring a pointer of type X, the compiler knows the size of data type X. Through pointer arithmetic, access to and iteration of complex data-types is no different. Pointers to functions and pointers to pointers work the same way. As you gain more experience, you will come to rely on pointers as a large part of your C toolbox, so it is well worth the time spent to understand the basics thoroughly. Passing pointers as function arguments is another entire facet of how pointers are used in C. Since this answer grew much longer than originally intended, we will leave that for another day. Let me know if you have any questions. Good luck with C.
In a computer, everything is a number if you look closely enough :)
A pointer is indeed a number. The only problem is, the size of that number depends on the hardware architecture and memory model.
For instance, some processors use 64 bit addresses, while microcontrollers might use only 16 bits.
Even on a 64 bits processor, a given process may be limited to 32 bits addresses (typically the WIN32 architecture that can run on a 64 bits processor).
If you try to print a pointer using %u, you have no guarantee the size of an integer will be equal to the size of a pointer. Typically on a WIN64 architecture %u would only display the first 32 bits of a pointer.
The %p format, on the other hand, takes the actual pointer size into account and guarantees its value will be displayed properly.
The portable way to print the address of an object is
int a, *p=&a;
printf("%p", (void *) p);
The "%p" specifier matches a void*. C does not provide a printf() specifier for int *, but all objects pointers can convert to a void*.
Many architectures use a linear address starting at 0 to some large value which may exceed INT_MAX. Yet C provides for many address schemes. The output of the above could be any of the below and not necessarily only made up of numeric characters:
123456789
0x0001C800
1000:0008
int:0000
The both is right.
Try this code:
int a = 10;
printf("%p\n%u", &a, &a);
will print the pointer address in hex ( %p ) and in uint ( %u ).
If you convert hex to uint, use calc of windows in developer mode,
you will have the same value.
Read this: https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays
Drink coffe and keep coding! =)
I'm confusing about casting of char to int pointer. I'm checking how pointer's casting works and the below code int to char is working fine.
#include <iostream>
using namespace std;
int main(){
int a=65;
void *p=&a;
cout << *static_cast<char*>(p);
}
Output
A
But when I try to cast from char to int it's not showing correct value.
#include <iostream>
using namespace std;
int main(){
char a='A';
void *p=&a;
cout << *static_cast<int*>(p);
}
What is the problem in the above code? Output is about garbage value.
First, you have to understand that the x86 architecture is what is called little-endian. This means that in multibyte variables, the bytes are ordered in memory from least to most significant. If you don't understand what that means, it'll become clear in a second.
A char is 8 bits -- one byte. When you store 'A' into one, it gets the value 0x41 and is happy. An int is larger; on many architectures it is 32 bits -- 4 bytes. When you assign the value 'A' to an int, it gets the value 0x00000041. This is numerically exactly the same, but there are three extra bytes of zeros in the int.
So your int contains 0x00000041. In memory, that is arranged in bytes, and because you're on a little-endian architecture, those bytes are arranged from least to most significant -- the opposite of how we normally write them! The memory actually looks like this:
+----+----+----+----+
int: | 41 | 00 | 00 | 00 |
+----+----+----+----+
+----+
char: | 41 |
+----+
When you take a pointer to the int and cast it to a char*, and then dereference it, the compiler will take the first byte of the int -- because chars are only one byte wide -- and print it out. The other three bytes get ignored! Now look back and notice that if the order of the bytes in the int were reversed, as on a big-endian architecture, you would have retrieved the value zero instead! So the behavior of this code -- the fact that the cast from int* to char* worked as you expected -- was strictly dependent on the machine you were running it on.
On the other hand, when you take a pointer to the char and cast it to an int*, and then defererence it, the compiler will grab the one byte in the char as you'd expect, but then it will also read three more bytes past it, because ints are four bytes wide! What is in those three bytes? You don't know! Your memory looks like this:
+----+
char: | 41 |
+----+
+----+----+----+----+
int: | 41 | ?? | ?? | ?? |
+----+----+----+----+
You get a garbage value in your int because you're reading memory that is uninitialized. On a different platform or under a different planetary alignment, your code might work perfectly fine, or it might segfault and crash. There's no telling. This is what is known as undefined behavior, and it is a dangerous game that we play with our compilers. We have to be very careful when working with memory on like this; there's nothing scarier than nondeterministic code.
You can safely represent anything as an array of char. It doesn't work the other way. This is part of the STRICT ALIASING rule.
You can read up on strict aliasing in other questions:
What is the strict aliasing rule?
More closely related to your question:
Once again: strict aliasing rule and char*
Quoting the answer given here: What is the strict aliasing rule?
[...] dereferencing a pointer that aliases another of an incompatible type is undefined behavior. Unfortunately, you can still code this way, maybe get some warnings, have it compile fine, only to have weird unexpected behavior when you run the code.
Also related to your question: Once again: strict aliasing rule and char*
Both C and C++ allow accessing any object type via char * (or specifically, an lvalue of type char). They do not allow accessing a char object via an arbitrary type. So yes, the rule is a "one way" rule.
(I must give credit for this second link to #Let_Me_Be)
Here when you are doing:
cout << *static_cast<int*>(p);
you are actually saying that p is pointing to an integer (represented by 4 bytes in memory) but you just written a char in it before (represented by 1 bytes in memory) so when you cast it to an integer you expanded your variable to 3 garbage bytes.
But if you cast it back to a char you will get your 'A' because you are slicing your int to a char:
cout << (char) *static_cast<int*>(p);
Otherwise if you just want the ASCII value, cast your void* to an char* (so when you dereference it you are only accessing 1 byte) and cast what is inside it to int.
char a = 'A';
void *p=&a;
cout << static_cast<int>(*((char*)p));
The fact is that static cast is able to understand that you want to cast an char to int (and get his ASCII value) but when asking a char* to int* he just change the number of bytes read when you dereference it.
According to Standards, casting a char (or multiple chars) to int is undefined behavior and therefore any result is allowed. Most compilers will try to do what makes sense, and so the following is a likely reason for the behavior you are seeing on your specific architecture:
Assuming a 32 bit int, an int is the same size as 4 chars
Different architectures will treat those four bytes differently to translate their value to an int, most commonly this is either little endian or big endian
Looking at:
[Byte1][Byte2][Byte3][Byte4]
The int value would either be:
(Little Endina) Byte1+Byte2*256+Byte3*256^2+Byte4*256^3
(Big Endian ) Byte4+Byte3*256+Byte2*256^2+Byte1*256^3
In your case either Byte1 or Byte4 is being set, the remaining bytes are whatever happens to be in memory since you are only reserving one byte where you need 4
Try the following:
int main(){
char a[4]={'A', 0, 0, 0};
void *p=a;
cout << *static_cast<int*>(p);
}
You may have to switch the initialization to {0,0,0, 'A'} to get what you want based on architecture
As noted, this is undefined behavior, but should work with most compilers and give you a better idea of what is going on under the hood
Consider following code:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
{
int a=65;
cout << hex << static_cast<int>(a) << "\n";
void *p=&a;
cout << hex << setfill('0') << setw(2 * sizeof(int)) << *static_cast<int*>(p) << "\n";
}
{
char a='A';
cout << hex << static_cast<int>(a) << "\n";
void *p=&a;
cout << hex << *static_cast<int*>(p) << "\n";
}
}
There is indeed 'A' character code (0x41) in the output, but its padded to the size of int with uninitialized values. You can see it, when you output the hexadecimal values of variables.
If I declare
int x = 5 ;
int* p = &x;
unsigned int y = 10 ;
cout << p+y ;
Is this a valid thing to do in C++, and if not, why?
It has no practical use, but is it possible?
The math is valid; the resulting pointer isn't.
When you say ptr + i (where ptr is an int*), that evaluates to the address of an int that's i * sizeof(int) bytes past ptr. In this case, since your pointer points to a single int rather than an array of them, you have no idea (and C++ doesn't say) what's at p+10.
If, however, you had something like
int ii[20] = { 0 };
int *p = ii;
unsigned int y = 10;
cout << p + y;
Then you'd have a pointer you could actually use, because it still points to some location within the array it originally pointed into.
What you are doing in your code snippet is not converting unsigned int to pointer. Instead you are incrementing a pointer by an integer offset, which is a perfectly valid thing to do. When you access the index of an array, you basically take the pointer to the first element and increase it by the integer index value. The result of this operation is another pointer.
If p is a pointer/array, the following two lines are equivalent and valid (supposing the pointed-to-array is large enough)
p[5] = 1;
*(p + 5) = 1;
To convert unsigned int to pointer, you must use a cast
unsigned int i = 5;
char *p = reinterpret_cast<char *>(i);
However this is dangerous. How do you know 5 is a valid address?
A pointer is represented in memory as an unsigned integer type, the address. You CAN store a pointer in an integer. However you must be careful that the integer data type is large enough to hold all the bits in a pointer. If unsigned int is 32-bits and pointers are 64-bits, some of the address information will be lost.
C++11 introduces a new type uintptr_t which is guaranteed to be big enough to hold a pointer. Thus it is safe to cast a pointer to uintptr_t and back again.
It is very rare (should be never in run-of-the-mill programming) that you need to store pointers in integers.
However, modifying pointers by integer offsets is totally valid and common.
Is this a valid thing to do in c++, and if not why?
Yes. cout << p+y; is valid as you can see trying to compile it. Actually p+y is so valid that *(p+y) can be translated to p[y] which is used in C-style arrays (not that I'm suggesting its use in C++).
Valid doesn't mean it actually make sense or that the resulting pointer is valid. Since p points to an int the resulting pointer will be an offset of sizeof(int) * 10 from the location of x. And you are not certain about what's in there.
A variable of type int is a variable capable of containing an integer value. A variable of type int* is a pointer to a variable copable of containing an integer value.
Every pointer type has the same size and contains the same stuff: A memory address, which the size is 4 bytes for 32-bit arquitectures and 8 bytes for 64-bit arquitectures. What distinguish them is the type of the variable they are poiting to.
Pointers are useful to address buffers and structures allocated dynamically at run time or any sort of variable that is to be used but is stored somewhere else and you have to tell where.
Arithmetic operations with pointers are possible, but they won't do what you think. For instance, summing + 1 to a pointer of type int will increase its value by sizeof(int), not by literally 1, because its a pointer, and the logic here is that you want the next object of this array.
For instance:
int a[] = { 10, 20, 30, 40 };
int *b = a;
printf("%d\n", *b);
b = b + 1;
printf("%d\n", *b);
It will output:
10
20
Because b is pointing to the integer value 10, and when you sum 1 to it, or any variable containing an integer, its then poiting to the next value, 20.
If you want to perform operations with the variable stored at b, you can use:
*b = *b + 3;
Now b is the same pointer, the address has not changed. But the array 10, 20, 30, 40 now contains the values 13, 20, 30, 40, because you increased the element b was poiting to by 3.