I'm interested in whether an unsigned long long
variable can be treated as an address, and the result seemed it was correct?
int var = 0;
// the variable var's address is 0x7fffffffe4a4 now in my system
unsigned long long ull = 0x7fffffffe4a4;
scanf("%d", ull); // I try to use ull as var's address and assign a value to var
printf("%d\n", var); // It seemingly works!
And I also conducted an experiment as follows.
int var = 0;
// the variable var's address is 0x7fffffffe4a4 now in my system
unsigned long long ull = 0x7fffffffe4a4;
int *ptr = &var;
// First, I want to know whether the memory used by unsigned long long and the one used by a pointer differ
printf("%zu\n", sizeof(ull));
printf("%zu\n", sizeof(ptr));
// The results are both "8" in my system
// Next I try to assign two different values to var by two ways
scanf("%d", ull); // I try to use x as an address to assign var
printf("%d\n", var);
scanf("%d", ptr); // And I also use a normal pointer to assign var
printf("%d\n", var);
// The results were in line with expectations!
Well, it seems an unsigned long long
variable can be used as a pointer successfully (although the compiler warned me that the argument was expected to be int*
rather than unsigned long long
), and I wonder …
What's the difference between a variable and a pointer at the hardware level? How are these two types of objects processed when they are stored and used? Who processes these objects and is it recommended to perform the operations above?
(In the end is it a feature of c?)
CodePudding user response:
On many (most) platforms, pointers and (unsigned) integers are stored in very similar formats at the hardware level (on your system, both an int*
pointer and an unsigned long long
are 8 bytes). However, from the point of view of the C language and compiler, they are very different types of variable.
One notable difference in their behaviour concerns arithmetic. For integral types, arithmetic operations like x = x 1
do exactly what you would naturally expect. However, for pointers, such operations are performed in base units of the size of the pointed-to type.
The following code demonstrates this (on a platform with 8-byte pointers and long long
and a 4-byte int
):
#include <stdio.h>
int main()
{
int myInt = 42;
int* ptr = &myInt;
unsigned long long ull = (unsigned long long)ptr;
printf("%p 6llX\n", (void*)ptr, ull);
ptr;
ull;
printf("%p 6llX\n", (void*)ptr, ull);
return 0;
}
The output is:
0000005B8A4FFC10 0000005B8A4FFC10
0000005B8A4FFC14 0000005B8A4FFC11
For the first line (as you have already noted), the two values are identical, and their binary representations will also be the same (on this platform). However, notice that the
increment behaves differently on the two types, so that the second line of output shows that the pointer has been incremented by 4
(the size of an int
) but the unsinged integer has been incremented by 1
.
CodePudding user response:
Here is a good analogy: using numbers instead of pointers is not wrong, it's dangerous. Imagine at the reason why you don't use a bicycle on a highway. Is it because you can't ride it? Of course you can, you have wheels, the asphalt is good. You don't do it because it's dangerous, the bicycle is not designed to do it.
For a more formal explanation:
Congratulations, you discovered that addresses are, ultimately, just numbers. The reason why in languages such as C or C (they are different!!) pointers are introduced is to avoid confusion and errors, letting the users and the compiler know specifically when they are dealing with an address or a number.
As I said at the beginning, at the end of the day, an address is a number that needs to tell the hardware where to look in the memory. However a pointer is a number that is treated with special care by the compiler:
You have the guarantee that a pointer has enough bits to store the address. Your example works "fine" with 64bits systems, but in a 16bits system a pointer will have the same range as a
unsigned short
value, and if you try to assign aunsigned long
you will run into all sorts of problems.Arithmetic on pointers follow the size of the underlying pointed data type. When you are pointing at a
short
at address0x100
and you want to go to the next byte, you know that you have to look at '0x102'.
short* ps = (short*)0x100;
ps = 1; // ps is now 0x102, because the compiler knows that short is 2 bytes
*ps = 0xAABB;
short s = 0x100;
// I need to advance to the next short.. mmm I have to do this:
s = sizeof(short).
*((short*)s) = 0xAABB; // Also, ugly syntax.
// See how easy it is to make errors when you use plain numbers?
Bottom line: pointers are special numbers with special properties, especially thought to handle memory accesses and address arithmetic.