Are these functions correct?

int Extension::GetTheAdressOfInt(int &a)
{
int b = (long) &a;
return b;
}

int Extension::GetTheAdressOfString(char* &a)
{
int b = (long) &a;
return b;
}

Are these correct?
No:
- pointer types can't safely be cast to long
- long can't safely be assigned to int
- The address of an object with type T has type T*, not int

C-style casts should be avoided
Prefer pointers to const char.

Usually, there's no need to bend over backwards to get the address of things. You can do that by writing &thing. In the special case of generic code, std::addressof(thing) works too.

One subtle point you got right was the use of a mutable lvalue reference.
If a const lvalue reference was used instead, the function would return a dangling pointer if a temporary was bound to the formal parameter. Not nice.

A more correct implementation might look like
1
2
int* address_of(int &i) { return &i; }
char const** address_of(char const* &s) { return &s; }


Last edited on
I knew that you could just use &x to get the adress of some variable with any data type and that it returns a pointer, that's why i'm doing this conversion. Because these functions should return a int and not a int* or a const char**, i also didn't knew that a pointer type can't safely be cast to long (the c++ shell didn't give a error) and that long can't safely be assigned to int (they have the same size on c++).

I did this to fix the unsafe casting:
1
2
3
4
5
6
7
8
9
10
11
int GetTheAdressOfInt(int &x)
{
    intptr_t offset = reinterpret_cast<intptr_t>(&x);
    return static_cast<int>(offset);
}

int GetTheAdressOfString(const char* &x)
{
    intptr_t offset = reinterpret_cast<intptr_t>(&x);
    return static_cast<int>(offset);
}

Is THIS one correct?
The c++ shell didn't give a error

The point of C++-style casts is that it's harder to ignore the type system with them, because they're very restrictive.

The C-style cast tries very hard to perform the conversion, even when that conversion would throw away data. Specifically, C-style casts relax the rules on the C++ casts and then apply them repeatedly until a valid conversion path is found. This usually works even when such conversions don't make any sense.

The problem is that the size of T*, int, and long are not necessarily the same size, and that narrowing conversions between them are likely to throw away information. Their actual size depends on the implementation's data model. My computer is one such example where they differ; both long and T* are 8 bytes, but int is 4 bytes.

intptr_t is an integer the same size as a plain pointer type, if it exists, but again converting it to int is likely to throw away information.

These functions should return a int

Then I imagine you're about as close as you're going to get.

Are you sure they can't be construed to return an intptr_t?
Do these objects belong to a single array, where an int (or std::ptrdiff_t) could be used to indicate their offset?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <memory>
#include <type_traits>
#include <iostream>

namespace Extension
{
    // int_ptr_val_t<T>: int if a valid pointer of type T* converted to int,
    //                   and then converted back to T* would yield the original pointer
    //                   std::uintptr_t otherwise.
    // this code assumes that the value representation of int uses all the bits
    // which are present in the object representation of int (generally true)
    template < typename T >
    using int_ptr_val_t = typename std::conditional< sizeof(int) >= sizeof(T*), int, std::uintptr_t >::type ;

    // return int if it is possible (to do so without incurring loss of information)
    template < typename T > int_ptr_val_t<T> address_as_int( T& object )
    { return reinterpret_cast< int_ptr_val_t<T> >( std::addressof(object) ) ; }

    //////////////////////////////////////////////////////////////////////////////////////
    // if these are really required:

    inline auto GetTheAdressOfInt( const int& a ) { return address_as_int(a) ; }

    using const_char = const char ;
    template < std::size_t N >
    inline auto GetTheAdressOfString( const_char(&a)[N] ) { return address_as_int(a) ; }

    inline auto GetTheAdressOfPtrToChar( char* const& a ) { return address_as_int(a) ; }

    // etc.

    ////////////////////////////////////////////////////////////////////////////////////////
}

int main()
{
    int i = 23 ;

    // this may result in *** error: narrowing conversion:
    // const int ptr_val = { Extension::GetTheAdressOfInt(i) } ;

    // this may result loss of information due to in narrowing:
    const int ptr_val_dubious = Extension::GetTheAdressOfInt(i) ;

    // this is fine:
    const auto ptr_val = Extension::GetTheAdressOfInt(i) ;

    std::cout << ptr_val << '\n' 
              << ptr_val_dubious << " (this may be an incorrect value)\n"  ;


    char cstr[25] {};
    std::cout << Extension::GetTheAdressOfString(cstr) << '\n' ;

    char* ptr = cstr ;
    std::cout << Extension::GetTheAdressOfPtrToChar(ptr) << '\n' ;
}

http://coliru.stacked-crooked.com/a/ce313858afa28444
Last edited on
I always advise to check twice for errors in the code while writing and not wait for the bugs and exceptions. There programs that can help you, like checkamrx for example but coding slowly and taking all possibilities in advance can save a lot of your time.
Good luck.
Topic archived. No new replies allowed.