Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointers to the same address are different from each other

Tags:

c++

I recently got curious about pointers and I cannot understand why the following doesn't work.

My idea is to create a variable, say its address (in decimal), ask the user to input it back and take that as the address for a pointer which points to the previous variable. In the end both variables should have the same value.

The last pointer points to supposedly, a bad memory address because it segfaults, but when I print the address of the pointers they are the same!

#include <iostream>

int main() {
    int temp = 0;
    printf("Temporary integer addr 0x%x (dec %d)\n", &temp, &temp);

    int input = -1;
    printf("Give me an address to read (in dec)\n");
    std::cin >> input;
    printf("You chose the number %d (hex 0x%x)\n", input, input);

    int* intPtr = (int*)input;
    int* tempPtr = &temp;

    if (intPtr == tempPtr) {
        printf("It works, they are equal!\n");
    } else {
        printf("Expected: 0x%x got 0x%x\n", tempPtr, intPtr);
        printf("Expected value: 0x%d got 0x%d", temp, *intPtr); // segfaults
    }

    return 0;
}

And this is the console output

Main function addr 0xb0ddf7d0 (dec -1327630384)
Temporary integer addr 0xb0ddf7c8 (dec -1327630392)
Give me an address to read (in dec)
-1327630392
You chose the number -1327630392 (hex 0xb0ddf7c8)
Expected: 0xb0ddf7c8 got 0xb0ddf7c8

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

What is wrong with it? How can I create a pointer that points to the address the user inputs? I compiled it on g++ (GCC) 8.2.1 20181127

like image 670
jamezrin Avatar asked Apr 16 '19 20:04

jamezrin


2 Answers

Your program has undefined behavior. First of all, the format specifier %x will cause printf to expect an argument of type unsigned int. However, you're passing it an argument of type int*. You'd want to use %p for printing that. Most likely, you're working on a 64-Bit machine, which means that addresses will be 64 Bits wide. An int will almost certainly not be large enough to represent all possible addresses. While passing an argument of one type to a function that will interpret the value of that argument as the wrong type is generally undefined behavior, what will most likely happen here in practice is that printf is simply going to print just the lower couple of Bits of your pointer value interpreted as an unsigned int. You then have your user type back in what is effectively only the lower half of your actual address, cast that back to a pointer, and access that memory location, which is almost certainly not going to be a valid address of an object of type int. That's where you get undefined behavior all over again (luckily, this time manifesting itself in a segfaul).

If you want an integer that can represent a pointer value, use, e.g., std::uintptr_t. Also, just don't use C-style casts in C++. They are there for backwards compatibility with C code. There is no advantage to C-style casts but a lot of disadvantages (see, e.g., this, or this, or this, or this question for more). Also, printf is not type safe, don't use printf. Your question is yet another testimony to that (see, e.g., here for more). If you had used std::cout instead, your pointer would have automatically been printed correctly. In general, printf expects you to take care of making sure the types of arguments that are actually passed matche the types specified in your format string. If you make any mistake at all, you end up in undefined behavior land. std::cout, on the other hand, will either do the right thing or fail to compile. Finally, I just can't help myself but note how really, really weird it is to see std::cin being used for input next to printf being used for output…

like image 106
Michael Kenzel Avatar answered Oct 13 '22 12:10

Michael Kenzel


int won't store a pointer on a 64 bit architecture. Usually you'll need longs (on LP64 architectures; long long is guaranteed to be at least 64 bits wide anywhere), or portably the intptr_t or uintptr_t typedef defined in stdint.h/cstdint if they're available.

If you need a larger type than int and you use int, the addresses will get truncated.

This is working for me:

#include <iostream>
#include <inttypes.h>
#include <stdint.h>

int main() {
    void* mainPtr = (void*) &main;
    printf("Main function addr 0x%" PRIxPTR " (" PRIdPTR " %ld)\n", (uintptr_t)&mainPtr, (uintptr_t)&mainPtr);

    int temp = 0;
    printf("Temporary integer addr 0x%" PRIxPTR " (dec %" PRIdPTR ")\n", (uintptr_t)&temp, (uintptr_t)&temp);

    uintptr_t input = -1;
    printf("Give me an address to read (in dec)\n");
    std::cin >> input;
    printf("You chose the number %" PRIdPTR " (hex 0x%" PRIxPTR ")\n", input, input);

    int* intPtr = (int*)input;
    int* tempPtr = &temp;

    if (intPtr == tempPtr) {
        printf("It works, they are equal!\n");
    } else {
        printf("Expected: 0x%" PRIxPTR " got 0x%" PRIdPTR "\n", (uintptr_t)tempPtr, (uintptr_t)intPtr);
        printf("Expected value: 0x%d got 0x%d", temp, *intPtr); // segfaults
    }

    return 0;
}

Note that the portable way to print these special typedefs is with the PRIxPTR and similar macros defined int inttypes.h/cinttypes, relying on string literal concatenation if you want to use stdio (C++'s std::cout << should print them with less hustle).

like image 35
PSkocik Avatar answered Oct 13 '22 12:10

PSkocik