types and casting

Hi guys,

so another casting question, in the below code I have a function addNumbers which returns the sum of the two arguments,

I cast the double* ( address of the doubles I want as arguments) to int*, when I do this , the value that is printed is pretty much garbage, it prints a large arbitrary number, what I thought would happen is that 59.3 would be truncated to 59 and 88.9 would be truncated to 88, but that isn't the case

could it be to do with the fact that a double is 8 bytes and an int is 4? but when casting a normal double to a normal int the value is just truncated, how come this isn't the case here?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include <iostream>

using namespace std;

int addNumbers(int* a,int* b){

   cout << *a << endl;

  return *a + *b;
}

int main()
{
    double a = 59.3;
    double b = 88.9;

    cout << a << endl;
    cout << addNumbers((int*)&a,(int*)&b) << endl;
}


when I use int* it works fine

1
2
3
4
5
6

    int x = 2;
    int y = 3;

    cout << printNumbers(&x,&y) << endl;


Last edited on
If you just want to truncate a double to an integer you just say:

1
2
double d = 1.23;
int n = (int)d;

But you are casting addresses, not values. In doing so you are reinterpreting the underlying bytes. The layout of a floating point value and the layout of an integer are totally different (in particular, a floating point value stores 2 different integers and a separate sign bit). It's pretty much meaningless to print out int-sized chunks of a double. Printing char-sized chunks (bytes) is potentially interesting, allowing you to see the underlying format of the value.

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <iomanip>

int main() {
    double d = 1.23;
    auto p = (unsigned char*)&d;
    std::cout << std::hex << std::setfill('0');
    for (int i = 0; i < 8; ++i)
        std::cout << std::setw(2) << (unsigned)(*p++) << '\n';
}

Thanks Dutch, that makes sense, I have a follow up


leading back to my recent question - http://www.cplusplus.com/forum/beginner/256829/

more specifically at - in.read((char*)&age,sizeof(int));

where age is originally a int variable,

how come here in this sense it is ok to convert an int pointer to a character(byte) pointer?

thanks
Last edited on
also..

I am trying to experiment around with casting pointer types to different pointer types but my compiler is stopping me from doing so, even a reinterpret cast will not work, from what I have seen some C compilers allow you to do this, how come my compiler is stopping me from doing so? and is there a way around this?

I am using a 32 bit mingw compiler


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


    int a = 150;
    int* p_a = &a;

    double b = 12.3;
    double* p_b = &b;

    char c = 'a';
    char* p_c = &c;

    p_b = (int*)p_b; //  does not work : cannot convert 'int*' to 'double*' in assignment
    p_b = reinterpret_cast<int*>(p_b); // also does not work : cannot convert 'int*' 
   // to'double*' in assignment

Look closely. I think you mean to assign the (int*) to p_a, not p_b. If you actually want to assign it to p_b then I have to ask what you think that would do? It would basically be meaningless.

how come here in this sense it is ok to convert an int pointer to a character(byte) pointer?

I'm not sure I understand. Why not? You can convert any pointer to a byte pointer.

The read function is a low-level input function that works byte-by-byte. You need to pass it a byte pointer (char*) to the first position of the memory you want to fill and a count of the number of bytes you want to read into that memory. It just reads bytes and stuffs them into memory, any bytes, any memory.

We can write the 32-bit integer normally and then read it in second half-first:

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
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdint> // for definite-sized types

int main() {
    const char* const Filename = "binary.bin";

    uint32_t n = 0x01020304;

    std::cout << std::hex << std::setfill('0');
    std::cout << std::setw(8) << n << '\n';     // 01020304

    // When opening for in|out mode, it fails if the file doesn't exist
    std::fstream bin(Filename, bin.binary | bin.in | bin.out);
    if (!bin) {
        bin.clear();
        // So we create the file in out mode, close it, and try to open it again.
        bin.open(Filename, bin.binary | bin.out);
        if (!bin) {
            std::cerr << "Cannot open file.\n";
            return 1;
        }
        bin.close();
        bin.open(Filename, bin.binary | bin.in | bin.out); // assuming success (!)
    }

    auto p = (char*)&n;
    bin.write(p, sizeof n); // write the 4 bytes starting at address p to the file

    bin.seekg(0);  // reset file position to the beginning

    n = 0; // so we can tell if the reads failed
    
    // read first half into second half and second half into first.
    bin.read(p+2, sizeof n / 2); // read 2 bytes from the file into bytes starting at p+2
    bin.read(p,   sizeof n / 2); // read the next 2 bytes into bytes starting at p

    std::cout << std::setw(8) << n << '\n';     // 03040102
}


You can even read and write an entire array:

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
#include <iostream>
#include <fstream>

int main() {
    const char* const Filename = "binary.bin";

    std::fstream bin(Filename, bin.binary | bin.in | bin.out);
    if (!bin) {
        bin.clear();
        bin.open(Filename, bin.binary | bin.out);
        if (!bin) {
            std::cerr << "Cannot open file.\n";
            return 1;
        }
        bin.close();
        bin.open(Filename, bin.binary | bin.in | bin.out);
    }

    int a[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    bin.write((char*)a, sizeof a); // sizeof returns the total number of bytes in a

    bin.seekg(0);  // reset file position to the beginning

    int b[100];
    bin.read((char*)b, sizeof a);
    
    for (int i = 0; i < int(sizeof a / sizeof *a); ++i)
        std::cout << b[i] << '\n';
}

Last edited on
1
2
3
4
5
6
7

// read first half into second half and second half into first.
    bin.read(p+2, sizeof n / 2); // read 2 bytes from the file into bytes starting at p+2
    bin.read(p,   sizeof n / 2); // read the next 2 bytes into bytes starting at p

    std::cout << std::setw(8) << n << '\n';     // 03040102


wow that is actually pretty neat :o, that is quite powerful,

is there any times where apart from reading and writing binary data that you would need to do an reinterpret cast? have you ever used it in a project?
Those pointer casts are all reinterpret_casts. Reinterpreting the underlying bytes of an object is potentially dangerous, mostly non-portable, and often avoidable. The basic evil of reinterpret_cast is that it totally sidesteps the type system, which is the heart and soul of C++. In a properly-structured C++ project, such casting would only exist at the lowest levels if at all.

In the previous code, we were using c-style casts which are frowned upon in C++ since they don't distinguish between radically different kinds of cast.

Real-life use cases can emerge at the interface between systems to handle hardware differences, such as swapping the endianness of a value (reversing the order of its bytes), although the bytes can also be moved around with shifts, etc. So there is often another option.
Last edited on
Topic archived. No new replies allowed.