4

Recently I have been working on sockets in C++ and I have come across this:

*(struct in_addr*)&serv_addr.sin_addr.s_addr = *(struct in_addr *)server->h_addr;

While this does do what I want it to I am a little confused as to why I can't do this:

(struct in_addr)serv_addr.sin_addr.s_addr = *(struct in_addr *)server->h_addr;

Since it becomes a pointer and then immediately is dereferenced shouldn't the second work as well as the first? I am still new to C++ and this is a little confusing to me. Any help would be greatly appreciated. Below is the code. All it does is takes the host name or IP and prints the IP to the screen.

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>


using namespace std;

int main(){

    int socketfd, portno, rwResult; 
    struct sockaddr_in serv_addr; 
    struct hostent* server;     
    char inputName[50];

    //The next block gets the host name and port number and stores them in variables
    cout<<"Enter host(Max Size 50): ";
    cin>>inputName;
    cout<<endl<<"Enter port number: ";
    cin>>portno;
    cout<<endl;

    server = gethostbyname(inputName);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(portno);

    *(struct in_addr*)&serv_addr.sin_addr.s_addr = *(struct in_addr *)server->h_addr;
    //This is where I am confused
    //(struct in_addr)serv_addr.sin_addr.s_addr = *(struct in_addr *)server->h_addr;


    cout<< "Server: "<<inet_ntoa(*(struct in_addr *)server->h_addr_list[0])<<endl;

    cout<< "Server: "<<inet_ntoa(*(struct in_addr *)&serv_addr.sin_addr.s_addr)<<endl;
    //The two cout's tell me if the address was copied correctly to serv_addr I believe.



    return 0;
}
Hudson Worden
  • 2,263
  • 8
  • 30
  • 45

3 Answers3

4

Objects can only be cast as another class if a constructor exists which accepts an argument of the original type.

Pointers, on the other hand, can be cast willy nilly. However, it can be very dangerous to do so. If it is necessary to cast, use static_cast or dynamic_cast when doing so, and potentially check to see if the cast was successful. One additional stylistic advantage to casting in this more explicit fashion is it makes the cast more blatant to someone browsing your code.

loki11
  • 374
  • 1
  • 4
3

What the code is doing is trying to re-interpret serv_addr.sin_addr.s_addr as a type in_addr. However, this conversion is not compatible. Therefore, you get a compiler error in the second snippet if you try a direct cast.

In the first snippet, you're essentially using pointer casting to get around this type incompatibility.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • So really it depends on what I am casting. Doing this would not work for everything. So in this instance I kind of got lucky? – Hudson Worden Sep 24 '11 at 01:16
  • It depends on how those struct members are setup. There are ways it can be done properly, but the code you've given us doesn't have the declarations of these structs. So I can't tell whether it's UB. – Mysticial Sep 24 '11 at 01:18
  • It's still not enough. `serv_addr.sin_addr.s_addr` shows that you have at least 3 nested structs. But you still haven't shown any of their declarations. EDIT: I take that back, it looks like they're part of a standard library? If so, I'm not familiar with them. – Mysticial Sep 24 '11 at 01:21
  • Actually this will almost always violate strict aliasing rules so "it does work" should carry a very big asterisk with it. – Voo Sep 24 '11 at 01:42
3

A simpler example might help to explain the difference:

double q = 0.5;
int n;

n = (int) q;    // #1
n = *(int*)(&q) // #2

The first version converts the value of q into a value of type int according to the rules of the language. Conversion is only possible for primitive types and for classes that define conversion operators/constructors.

The second version reinterprets the binary representation of q as an integer. In good cases (like in your example) this produces something useful, but in general this is undefined behaviour, as it is not allowed to access a variable of one type through a pointer to a different type.

Your example may be valid because the two types in question are both POD structs with the same initial members, and you are accessing only one of the common initial members. Edit. I checked, server->h_addr is of type char *. It's possible that this just serves as a placeholder and the pointer is in fact to a structure of the correct type.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084