Miao_txy
Miao_txy

Reputation: 71

Why can this kind of declaration work in C++?

Passing an int to an uninitialized pointer cannot work. But passing a reference to an uninitialized pointer can work.

What's the mechanism behind this?

    int a = 1;
    int &r = a;
    cout << &a << " " << &r << endl; // 0x61ff10 0x61ff10

    // can work
    int *p1;
    *p1 = r;
    cout << p1 << endl; // 0x61ff60

    // cannot work
    int *p2;
    *p2 = a;
    return 0;

The code below is how I tested these strange concepts page 400 of cpp primer plus.

const free_throws & clone(free_throws & ft)
{
    free_throws * pt;
    *pt = ft; // copy info
    return *pt; // return reference to copy
}

P.S.: I tried changing the value of a and cout << *p1 always outputs the correct value:

    int a = 3;
    int &r = a;
    cout << &a << " " << &r << endl;
    // can work
    int *p1;
    *p1 = r;
    cout << p1 << endl;

    cout << *p1; // always the right value

Upvotes: 2

Views: 171

Answers (4)

Victor
Victor

Reputation: 470

It is an undefined behavior in both cases, as *p1 and *p2 are uninitialized.

Check the instructions generated:

int a = 1; Variable a is stored and initialized in rbp-28:

    mov     DWORD PTR [rbp-28], 1

int &r = a; Then, alias r is stored in rbp-8, which is initialized reading &a using lea instruction:

    lea     rax, [rbp-28]
    mov     QWORD PTR [rbp-8], rax

int *p1; *p1 = r; Pointer *p1 is stored in rbp-16. The last mov assigns *p1 = a without initializing p1 to any value:

    mov     rax, QWORD PTR [rbp-8]    # rax = address of r
    mov     edx, DWORD PTR [rax]      # edx = value of a (deferences r)
    mov     rax, QWORD PTR [rbp-16]   # rax = address of p1
    mov     DWORD PTR [rax], edx      # rax = p1, [rax] = *p1, edx = a

int *p2; *p2 = a; Pointer *p2 is stored in rbp-24. As with p1, p2 is assigned a value without pointing first to any valid address:

    mov     edx, DWORD PTR [rbp-28]  # edx = value a
    mov     rax, QWORD PTR [rbp-24]  # rax = address of p2
    mov     DWORD PTR [rax], edx     # rax = p2, [rax] = *p2, edx = a

Upvotes: 0

t.niese
t.niese

Reputation: 40842

This Code from C++ Primer Plus (You shoudln't confuse "C++ Primer" a recommended book with "C++ Primer Plus") is not valid:

const free_throws & clone(free_throws & ft)
{
    free_throws * pt;
    *pt = ft; // copy info
    return *pt; // return reference to copy
}

The text in the book above the shown code says:

A second method is to use new to create new storage. You’ve already seen examples in which new creates space for a string and the function returns a pointer to that space. Here’s how you could do something similar with a reference

So the code meant to look like that (in an early revision of the book the new was there):

const free_throws & clone(free_throws & ft)
{
    free_throws * pt = new free_throws();
    *pt = ft; // copy info
    return *pt; // return reference to copy
}

If new is missing then it is undefined behavior.

After the code the book also mentions that:

This makes jolly a reference to the new structure. There is a problem with this approach: You should use delete to free memory allocated by new when the memory is no longer needed.

So even with the new it is a really bad code style.

i tried to change the value of a and cout << *p1 can always output the correct value

int *p1; *p1 = r; is undefined behavior, the compiler could make wrong assumptions about the code due to that, resulting in the compiler or optimizer create an unexpected/unpredictable machine code.

But what will most likely happen in practice for the shown code is: You don't initialize int *p1; so p1 holds an undetermined value, this means it points to an arbitrary location in memory. If you are lucky it points to a valid memory address of currently not used memory. With *p1 = r; you write to the memory at that memory address, if you are lucky nothing important is at that address so nothing bad happens, but you still write at a "random" position in memory. So you might get the correct result, but your code is still not valid.

But that's just one possible outcome that could happen.

Upvotes: 4

Talal02
Talal02

Reputation: 162

Link To alias Pointers

int a = 1;
int &r = a; // alias pointers reference above
cout << &a << " " << &r << endl; // 0x61ff10 0x61ff10 (Both address are same)


// It will also not work because you are not initializing p1
int *p1;
*p1 = r;
cout << p1 << endl; // Segmentation Fault

// Same but working code
int *p1 = &r;
cout << p1 << endl; // 0x61ff60


// cannot work (Because you have not initialized p2)
int *p2;
*p2 = a;

// Initialize p2 with a directly
int *p2 = &a; 
cout << p2 << endl; // This time same address as of a
// or
int *p2 = new int;
*p2 = a;
cout << p2 << endl; // This time different address but same value of a

Upvotes: -2

bolov
bolov

Reputation: 75678

int a = 1;
int &r = a;
cout << &a << " " << &r << endl;

This is ok as every variable is initialized.


int *p1;
*p1 = r;
cout << p1 << endl;

This is incorrect because p1 is uninitialized. Accessing it (i.e. *p1 in your example) causes the program to have Undefined Behavior. Undefined behavior means anything can happen: the program can segfault, it can appear to work, it can print gibberish, really anything.


int *p2;
*p2 = a;

Same as previous. Access of uninitialized variable resulting in Undefined Behavior.


free_throws * pt;
*pt = ft;

Same as previous. Access of uninitialized variable resulting in Undefined Behavior.

Upvotes: 0

Related Questions