Thread: Question regarding a this C program

  1. #1
    Registered User
    Join Date
    Sep 2008
    Posts
    14

    Question regarding a this C program

    Hi,
    I am currently looking through a simple C program which basically is a very basic nslookup tool. My question is regarding on how some of the C code in this is working.
    Here is the pieces of code:
    Code:
    /* Bare nslookup utility (w/ minimal error checking) */
    #include <stdio.h>          /* stderr, stdout */
    #include <netdb.h>          /* hostent struct, gethostbyname() */
    #include <arpa/inet.h>      /* inet_ntoa() to format IP address */
    #include <netinet/in.h>     /* in_addr structure */
        int main(int argc, char **argv) {
            struct hostent *host;     /* host information */
            struct in_addr h_addr;    /* internet address */
                if (argc != 2) {
                  fprintf(stderr, "USAGE: nslookup <inet_address>\n");
                  exit(1);
                }
                if ((host = gethostbyname(argv[1])) == NULL) {
                  fprintf(stderr, "(mini) nslookup failed on '%s'\n", argv[1]);
                  exit(1);
                }
                h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
                fprintf(stdout, "%s\n", inet_ntoa(h_addr));
                exit(0);
              }
    The in_addr structure looks like this:
    Code:
    struct in_addr {
              __u32   s_addr;
      };
    I would like to know how does the following line of the code works and if someone could break it down how to read this line:
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    The code defines the following structure: struct hostent *host;
    Which looks like this:
    Code:
    struct hostent
     {
        char *h_name;                 /* Official name of host.  */
        char **h_aliases;             /* Alias list.  */
        int h_addrtype;               /* Host address type.  */
        int h_length;                 /* Length of address.  */
        char **h_addr_list;           /* List of addresses from name server.  */
        #define h_addr  h_addr_list[0]  /* Address, for backward compatibility.  */
     };
    Here is the function prototype for gethostbyname function:
    Code:
     /* Return entry from host data base for host with NAME.
        This function is a possible cancellation point and therefore not
        marked with __THROW.  */
     extern struct hostent *gethostbyname (__const char *__name);
    If i understand it correctly this function returns a point to structure hostent. Is this correct and all the values are populated in the hostent structure shown above.

    What does is type is __u32 this is in struct in_addr?

    So the argv[1] is passed to gethostbyname() funtion which returns a pointer to structure and the address is stored in the h_addr_list[0] and this assigned to h_addr and this is passed to net_ntoa(h_addr) to print the output in correct format. Is this correct understanding?

    Thanks

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Just like anything else, you have to start at the inside of parentheses and then go out:
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    Start with the variable host, follow the pointer, and get h_addr_list[0].
    Cast this pointer into a pointer to unsigned long *. (Same memory address, so nothing happens ... yet.)
    Follow the pointer to get the value. Since we are now pretending this is an unsigned long *, we get however-many-bytes (I'm guessing 4) and using it as a single number.

    You'll have to read the header to find the definition of __u32, but just from the name I would bet any amount that it's "unsigned <whatever type is 32-bits on your system>".

    So the argv[1] is passed to gethostbyname() funtion which returns a pointer to structure and the address is stored in the h_addr_list[0] and this assigned to h_addr and this is passed to net_ntoa(h_addr) to print the output in correct format. Is this correct understanding?
    Sort of; h_addr_list[0] is the first address stored; as you can tell from the name, h_addr_list is (or can be) a list of a bunch of addresses. h_addr is defined to be the first one on the list, yes.

  3. #3
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Quote Originally Posted by tabstop View Post
    Just like anything else, you have to start at the inside of parentheses and then go out:
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    Start with the variable host, follow the pointer, and get h_addr_list[0].
    Cast this pointer into a pointer to unsigned long *. (Same memory address, so nothing happens ... yet.)
    Follow the pointer to get the value. Since we are now pretending this is an unsigned long *, we get however-many-bytes (I'm guessing 4) and using it as a single number.

    You'll have to read the header to find the definition of __u32, but just from the name I would bet any amount that it's "unsigned <whatever type is 32-bits on your system>".


    Sort of; h_addr_list[0] is the first address stored; as you can tell from the name, h_addr_list is (or can be) a list of a bunch of addresses. h_addr is defined to be the first one on the list, yes.
    Hi,
    Thanks for your reply.
    So the following line i can read it as:
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    host is pointer to pointer to unsigned long or is it host is pointer to unsigned long
    So does this mean that h_addr.saddr = pointer or a value which is basically h_addr_list[0]?

    How can i print to the screen the value of h_addr, i.e what is the format specifier for __u32? I checked this out on my system and it is a unsigned 32 bit int.

    The inet_ntoa function is defined as follows:
    Code:
    char *inet_ntoa(struct in_addr in);
    
    The inet_ntoa() function converts the Internet host address in given in network byte order to a string in standard numbers-and-dots notation. The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
    So the inet_ntoa returns a pointer to a char....Is this correct?

    Thanks for your help

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Sigh.
    Realize that memory is only raw - it does not have any type information, it's just bits. So information is stored there in bits, and the compiler keeps track of the type ONLY by looking at the type of the variable the data is stored inside.
    This means that you can tell the compiler to treat the data in another way. That's done via the cast.
    And it's a pointer to unsigned long, which is then dereferenced to get the data.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by cnb View Post
    Hi,
    Thanks for your reply.
    So the following line i can read it as:
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    host is pointer to pointer to unsigned long or is it host is pointer to unsigned long
    host is a pointer to a struct hostent. Remember that *whatever is not a pointer -- it is the value of the piece of memory pointed to by whatever.
    So does this mean that h_addr.saddr = pointer or a value which is basically h_addr_list[0]?
    saddr is a __u32.
    How can i print to the screen the value of h_addr, i.e what is the format specifier for __u32? I checked this out on my system and it is a unsigned 32 bit int.
    The format specifier for unsigned int is %u, or %x if you want it in hexadecimal (or %o if you want it in octal, but I doubt that you do).
    The inet_ntoa function is defined as follows:
    Code:
    char *inet_ntoa(struct in_addr in);
    
    The inet_ntoa() function converts the Internet host address in given in network byte order to a string in standard numbers-and-dots notation. The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
    So the inet_ntoa returns a pointer to a char....Is this correct?
    Correct. More importantly, that pointer-to-char is interpreted as a string (as the man page there indicates), so you have consecutive chars terminated by a \0 character, and the address of the first one is returned to you.

  6. #6
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Hi,
    Thanks for your help....just one more further clarification
    Code:
    h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
    I am currently looking at the following website which tells some of the rules in terms of how to read C type declarations:
    http://unixwiz.net/techtips/reading-cdecl.html
    How do i apply this rule to the above piece of code.....i.e. how do i treat the "->" symbol?
    So i start by applying the rule to the following piece:
    Code:
    (unsigned long *) host
    So start with variable host and than move to right in this case its "->" do not know i treat this? but if i move to left than it is host is pointer to unsigned long.

    Is there any other easy way to extract the value from h_addr_list[0] and assign it to h_addr?

    Thanks

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Is there any other easy way to extract the value from h_addr_list[0] and assign it to h_addr?
    Yes.
    Code:
    h_addr.s_addr = *((unsigned long*)host->h_addr_list[0]);
    What is difficult understanding here?
    This: (unsigned long*) is a cast. It vasts the right expression into the given type you specify.
    So it converts the expression host->h_addr_list[0] to unsigned long*.
    Now, host is a pointer to a struct and we want to access one of its members. Therefore we use the -> syntax. It's a shortcut used on pointers since the * operator has lower precedence than the . operator, therefore you must write (*var).something.
    h_addr_list[0] is of the wrong type to us, so we take its value and cast it to the right type, unsigned long*.
    Now we want to assign the value it's pointing to, so we must dereference it.

    It's not difficult, you just have to read up on pointers a bit to get the grasp on the whole.
    This was already explained to you.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Quote Originally Posted by Elysia View Post
    Yes.
    Code:
    h_addr.s_addr = *((unsigned long*)host->h_addr_list[0]);
    What is difficult understanding here?
    This: (unsigned long*) is a cast. It vasts the right expression into the given type you specify.
    So it converts the expression host->h_addr_list[0] to unsigned long*.
    Now, host is a pointer to a struct and we want to access one of its members. Therefore we use the -> syntax. It's a shortcut used on pointers since the * operator has lower precedence than the . operator, therefore you must write (*var).something.
    h_addr_list[0] is of the wrong type to us, so we take its value and cast it to the right type, unsigned long*.
    Now we want to assign the value it's pointing to, so we must dereference it.

    It's not difficult, you just have to read up on pointers a bit to get the grasp on the whole.
    This was already explained to you.
    So the reason for converting expression host->h_addr_list[0] to unsigned long* is because h_addr.s_addr is a type __u32 is this why the cast is used to convert the expression to unsigned long?
    So outer brackets i.e.
    Code:
     h_addr.s_addr = *(expression);
    So this just a normal pointer deference that basically says allocate the value pointed by this pointer to h_addr.s_addr variable. Is this correct?
    So the expression is just accessing one of the members from host structure and making sure the type is correct by using cast and this is to match the type of h_addr.s_addr?

    Thanks

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by cnb View Post
    So the reason for converting expression host->h_addr_list[0] to unsigned long* is because h_addr.s_addr is a type __u32 is this why the cast is used to convert the expression to unsigned long?
    Yes, it is. As for why it isn't unsigned long* in the first place, I don't know. I haven't studied sockets programming. But it's backward compatibility or some C workaround for something.

    So outer brackets i.e.
    Code:
     h_addr.s_addr = *(expression);
    So this just a normal pointer deference that basically says allocate the value pointed by this pointer to h_addr.s_addr variable. Is this correct?
    So the expression is just accessing one of the members from host structure and making sure the type is correct by using cast and this is to match the type of h_addr.s_addr?

    Thanks
    Yes, except it's not allocating, it's assigning.
    Assign the value pointed to by the host->h_addr_list[0] to h_addr.s_addr.
    Allocating means reserving memory, ie malloc.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Quote Originally Posted by Elysia View Post
    Yes, it is. As for why it isn't unsigned long* in the first place, I don't know. I haven't studied sockets programming. But it's backward compatibility or some C workaround for something.


    Yes, except it's not allocating, it's assigning.
    Assign the value pointed to by the host->h_addr_list[0] to h_addr.s_addr.
    Allocating means reserving memory, ie malloc.
    Hi,
    Thanks alot for your help....
    I have been reading more on the cast operator and in the oreilly book that i have on C programming has a small section which describes the cast operator in C. Now the example given is very simple i.e. it show how to use the cast operator to change between int and long.
    In the code that we have been discussing so far i.e.:
    Code:
     (unsigned long*)host
    My only question is what if this was like this:
    Code:
     (unsigned long) *host
    Will this be wrong? Is there rules on how to use cast operator with pointers? My book does not mention how to use cast operator with pointers? Where could i find some more information on this?

    Thanks
    Last edited by cnb; 10-11-2008 at 04:42 AM.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Of course it's wrong.
    They don't do the same thing.

    Let's just define the type of host to T*, OK?
    In the second example, first you dereference host, so you get T. Then you cast it to unsigned long. Because it's a cast, the compiler may change the data or truncate the data to fit into unsigned long.
    In the first example, you cast the address of host, the pointer, to unsigned long. This means the compiler will treat the information stored as unsigned long and then dereference to get an unsigned long.

    This example clearly outlines the problems you may encounter.
    Example:
    Code:
    	double* x = (double*)malloc( sizeof(double) ); //new double;
    	*x = 1.2345678;
    
    	unsigned __int64 y = (unsigned long)*x; // What do you think this will do?
    	printf("&#37;f\n%f\n", *x, *(double*)&y);
    
    	unsigned __int64 z = *(unsigned __int64*)x;
    	printf("%f\n%f\n", *x, *(double*)&z);
    
    	free(x);
    Output:
    1.234568
    0.000000
    1.234568
    1.234568
    Last edited by Elysia; 10-11-2008 at 05:01 AM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. newb question: probs with this program
    By ajguerrero in forum C Programming
    Replies: 5
    Last Post: 04-19-2006, 08:04 AM
  2. Random Question Assign Program
    By mikeprogram in forum C++ Programming
    Replies: 6
    Last Post: 11-17-2005, 10:04 PM
  3. I'm not ask for ENTIRE program, only 1 Question !
    By Th3-SeA in forum C Programming
    Replies: 10
    Last Post: 10-01-2003, 12:33 PM
  4. Question type program for beginners
    By Kirdra in forum C++ Programming
    Replies: 7
    Last Post: 09-15-2002, 05:10 AM
  5. Replies: 8
    Last Post: 03-26-2002, 07:55 AM