C Board  

Go Back   C Board > General Programming Boards > C Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 10-10-2008, 12:26 AM   #1
cnb
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
cnb is offline   Reply With Quote
Old 10-10-2008, 12:37 AM   #2
and the Hat of Guessing
 
tabstop's Avatar
 
Join Date: Nov 2007
Posts: 8,862
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>".

Quote:
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.
tabstop is offline   Reply With Quote
Old 10-10-2008, 04:12 AM   #3
cnb
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
cnb is offline   Reply With Quote
Old 10-10-2008, 04:18 AM   #4
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,785
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.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 10-10-2008, 06:59 AM   #5
and the Hat of Guessing
 
tabstop's Avatar
 
Join Date: Nov 2007
Posts: 8,862
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.
Quote:
So does this mean that h_addr.saddr = pointer or a value which is basically h_addr_list[0]?
saddr is a __u32.
Quote:
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).
Quote:
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.
tabstop is offline   Reply With Quote
Old 10-10-2008, 03:07 PM   #6
cnb
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
cnb is offline   Reply With Quote
Old 10-10-2008, 03:17 PM   #7
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,785
Quote:
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.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 10-10-2008, 06:07 PM   #8
cnb
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
cnb is offline   Reply With Quote
Old 10-11-2008, 02:37 AM   #9
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,785
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.

Quote:
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.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 10-11-2008, 04:39 AM   #10
cnb
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.
cnb is offline   Reply With Quote
Old 10-11-2008, 04:43 AM   #11
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,785
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("%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
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.

Last edited by Elysia; 10-11-2008 at 05:01 AM.
Elysia is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
newb question: probs with this program ajguerrero C Programming 5 04-19-2006 08:04 AM
Random Question Assign Program mikeprogram C++ Programming 6 11-17-2005 10:04 PM
I'm not ask for ENTIRE program, only 1 Question ! Th3-SeA C Programming 10 10-01-2003 12:33 PM
Question type program for beginners Kirdra C++ Programming 7 09-15-2002 05:10 AM
N00b Question. Can't get this program to work with me !!! Halo C Programming 8 03-26-2002 07:55 AM


All times are GMT -6. The time now is 10:09 PM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.2

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