Ubgreat
Ubgreat

Reputation: 63

Can I treat an 'unsigned long long' variable as an address to assign another variable?

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?)

Upvotes: 6

Views: 1946

Answers (4)

Steve Summit
Steve Summit

Reputation: 48073

There are two fundamental issues here:

  1. Interpretation matters. In almost any programming language, the values we work with are not just raw bit patterns, they are bit patterns interpreted as a particular type. For example, consider the binary number 0b111111101000000000000000000000, or in hexadecimal, 0x3fa00000. Interpreted as an int, that's the number 1067450368. Interpreted as a float, it's 1.25. Same bit pattern, two completely different, totally unrelated numbers. You could take the same bit pattern and try to interpret it as a pointer, but that would be a third, completely unrelated interpretation, and it wouldn't necessarily mean anything, either.
  2. Just because you have a pointer value, pointing at (or containing) a certain memory address, doesn't necessarily mean you can actually access that memory. It might be in use by some other variable in your program. It might be in use by the actual code of your program. It might be in use by some other program. Or it might not exist at all — it might refer to a memory address that's higher than the total amount of memory in your computer.

So taking an integer value, converting it to a pointer, and then accessing the resulting memory is sort of like chipping golf balls off the top of a tall building: you have no idea where the balls are going to land, and they might hurt someone, and it's therefore a pretty irresponsible practice.

Upvotes: 1

markoj
markoj

Reputation: 148

They have the same size on certain platforms, and can then be typecast into the other without disregarding any number of bits. There is the size_t type, which represents the "return" type of sizeof and the is used to store size used in memory(in bytes) for a variable. size_t can be only 2 bytes big(sizeof(size_t)=2) on some platforms, but long long(whose sizeof is the same as the sizeof of unsigned long long) is always 8 or higher. It makes sense for pointers to any type to have the same sizeof. The number of bytes in the memory before the first byte referred to by the pointer is stored in the pointer, so it makes sense that its sizeof should be same as sizeof of a pointer to any type. So, I'd recommend using size_t if you want to store an address in an integral type.

Upvotes: 1

Fra93
Fra93

Reputation: 2082

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 a unsigned 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 address 0x100 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.

Upvotes: 6

Adrian Mole
Adrian Mole

Reputation: 51904

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 %016llX\n", (void*)ptr, ull);
    ++ptr;
    ++ull;
    printf("%p %016llX\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.

Upvotes: 7

Related Questions