2

Here is my code:

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

int main (void) {

  struct addrinfo hints; 
  memset (&hints, 0, sizeof hints);

  hints.ai_family = AF_UNSPEC; 
  hints.ai_socktype = SOCK_DGRAM;  
  hints.ai_flags = AI_CANONNAME;   

  struct addrinfo *res;

  getaddrinfo ("example.com", "http", &hints, &res);
  printf ("Host: %s\n", "example.com");

  void *ptr;

  while (res != NULL) {

    printf("AI Family for current addrinfo: %i\n", res->ai_family);

    switch (res->ai_family) {
      case AF_INET:
        struct sockaddr_in *sockAddrIn = (struct sockaddr_in *) res->ai_addr;
        printf("Port number: %u\n", ntohs(sockAddrIn->sin_port));
        printf("IP address is: %u\n", ntohs(sockAddrIn->sin_addr.s_addr));
        break;
    }
    res = res->ai_next;
  }

  return 0;
}

And in action:

$ gcc ex3.c
$ ./a.out
Host: example.com
AI Family for current addrinfo: 2
Port number: 80
IP address is: 23992
AI Family for current addrinfo: 30

This is all fine, however this does not even compile:

void *ptr;

while (res != NULL) {

printf("AI Family for current addrinfo: %i\n", res->ai_family);

switch (res->ai_family) {
  case AF_INET:
    ptr = (struct sockaddr_in *) res->ai_addr;
    printf("Port number: %u\n", ntohs(ptr->sin_port));
    printf("IP address is: %u\n", ntohs(ptr->sin_addr.s_addr));
    break;
}
res = res->ai_next;
}

What I am trying to do is to cast a void pointer to a struct sockaddr_in *. The compile error is:

ex3.c:33:48: error: member reference base type 'void' is not a structure or union

printf("IP address is: %u\n", ntohs(ptr->sin_addr.s_addr));

What am I doing wrong? Why is the pointer ptr not casted?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Koray Tugay
  • 22,894
  • 45
  • 188
  • 319

5 Answers5

4

Type of ptr is void *, you need to add a cast to appropriate type before deferencing it.

Recall that: C does not allow dereferencing incomplete types. And void as per C specs is an incomplete type.

One simple fix is to change the type of ptr to struct sockaddr_in *.

You can also add cast in the printf line itself, but this will add unnecessary cluttering to the code.

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
4

This line:

ptr = (struct sockaddr_in *) res->ai_addr;

does not do what you think it does; it doesn't automagically make ptr be of type struct sockaddr_in *, so you're still trying to dereference a void pointer. What you need to do is

printf("Port number: %u\n", ntohs(((struct sockaddr_in *) res->ai_addr)->sin_port));
user4520
  • 3,401
  • 1
  • 27
  • 50
  • Just one more question if I may. Why is this printf("IP address is: %u\n", ntohs(sockAddrIn->sin_addr.s_addr)); still printing 23992? I was hoping to get something like 120.32.3.2 .. – Koray Tugay Jun 09 '15 at 07:56
  • 1
    @KorayTugay Two problems - first, `ntohs` stands for `network to host short` - short meaning two bytes, as is the case with ports. You want `ntohl`. Second, IPv4 addresses are in fact 32 bit integers. To convert it to the decimal dot notation, which is what we're used to, you can use `inet_ntoa`, or better yet, `inet_ntop` (though the former has simpler syntax). Note that if you use these functions, you don't need to convert `sin_addr` to host byte order since they handle that for you. – user4520 Jun 09 '15 at 07:58
2

Why is the pointer ptr not casted?

There is apparently no issue with the casting itself here. The problem is the usage of the void pointer ptr later in the code.

In your case, ptr is a pointer to a void type. It does not have any type information, as compared to a struct pointer. Putting a pointer with casting into a void * variable does not change the type of the void * variable itself. It still remains an incomplete type, which cannot be used as an operand to member (de)reference operator.

In other words, a void type cannot have a member, so the member reference is outright wrong.

I don't see any use of making ptr a void *. Maybe , you want to define ptr as

 struct sockaddr_in * ptr = NULL;

to get around the issue.

That said, %u is not a format specifier to be used with a uint16_t type (return type of ntohs()). You may want to use PRIu16 as the format specifier.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Here is the cast: ptr = (struct sockaddr_in *) res->ai_addr; – Koray Tugay Jun 09 '15 at 07:42
  • @KorayTugay the _cast_ does not change the _type_ of `ptr`, does it? :-) – Sourav Ghosh Jun 09 '15 at 07:42
  • @KorayTugay Not much - assigns to value of `res->ai_addr` to `ptr`, but `ptr`'s _type_ remains the same. – user4520 Jun 09 '15 at 07:44
  • @KorayTugay It tells to _interpret_ the `res->ai_addr;` as a pointer to `struct sockaddr_in`, but `ptr` stays a `void *`. – Sourav Ghosh Jun 09 '15 at 07:44
  • But in this post: http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc one answer says: void * is automatically and safely promoted to any other pointer type. – Koray Tugay Jun 09 '15 at 07:49
  • @KorayTugay That's different - you're converting _from_ `void*` _to_ a particular pointer type. In C, no force can change the type of a variable; once you make it a `void*`, it stays that way. – user4520 Jun 09 '15 at 07:51
  • @szczurcio Ah I see, confusing, isn't it.. Thanks for the clarification. So a void* can not do much I assume? – Koray Tugay Jun 09 '15 at 07:54
  • `void*` is a very powerful concept in C, but you need to tell the compiler what to treat the data type as. When you do `ptr->sin_addr`, the compiler is confused. What's `sin_addr` in the context of `void*`? What is its size? Before you dereference a `void*`, it has to be cast to the right type so the compiler knows how to handle it. – user4520 Jun 09 '15 at 07:56
  • @szczurcio ok thanks a lot. I will do some more reading on this topic as I see that I am confused. – Koray Tugay Jun 09 '15 at 07:58
2

Change the declaration of ptr from

void * ptr;

to

sockaddr_in* ptr;
justin.m.chase
  • 13,061
  • 8
  • 52
  • 100
1

ptr = (struct sockaddr_in *) res->ai_addr;

this is your error. ptr is a void pointer and you are going to assign a struct pointer to it. So you must first create a struct pointer and the assign the result from your cast to that.Do

struct sockaddr_in *ptr;
ptr = (struct sockaddr_in *) res->ai_addr;
Jagath01234
  • 139
  • 1
  • 10