Type Casting IPv4 and IPv6 structures to Generic Structures

T

tweak

I'm struggling with the concept of typecasting when
setting up a TCP client.

Here's a code snip (modified from W. Richard Stevens Unix Programming
book) to demonstrate where I am struggling:

int sockfd;
struct sockaddr_in servaddr;

bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); /* PORT 13 */

/* argv[1] = "127.0.0.1" */

if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr)) <= 0 ) {
perror("inet_pton()");
exit(1);
}

if ( (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) <
0) {
perror("connect()");
exit(1);
}

When this line occurs:

(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

Can anyone help me out?

I stepped through the code creating a pointer of type struct sockaddr
and pointed it to the address of servaddr. But I do not understand
why sa_data[14] would have data looking like \111\0\r\0\0 . . .
nor do I understand how to convert that string back to it's original
form.

Any help would be appreciated.

Thanks,

Brian

P.S. I'm compiling using gcc, and debuggin with gdb.
 
G

Grumble

tweak said:
I'm struggling with the concept of typecasting when setting
up a TCP client.

Here's a code snip (modified from W. Richard Stevens Unix
Programming book) to demonstrate where I am struggling:

Try comp.unix.programmer or (perhaps) comp.os.linux.development.apps
 
T

tweak

I do not see why typecasting structures would be specific to unix.
You should be able to do this across platforms. I am posting here
to find out about the conversion between the two structures. I
do not understand the integer to char[] type conversion happening
when I write:

(struct sockaddr *) &servaddr

I can setup a pointer of type struct sockaddr and see in gdb
the conversion. I just don't understand it.

Here's some code to do that;

struct sockaddr *test;

test = (struct sockaddr *) &servaddr

the above just passes the address to the pointer test.

Now in gdb, I can do a display of *test and see what's
in the structure. But I do not understand the char[]
conversion.

Thanks,

Brian

P.S. I posted this thread again under a different name, hoping
to get general C answers about the conversion without scaring
away folks that haven't messed with socket programming.
 
S

Stephen Sprunk

tweak said:
I do not see why typecasting structures would be specific to unix.
You should be able to do this across platforms. I am posting here
to find out about the conversion between the two structures. I
do not understand the integer to char[] type conversion happening
when I write:

To avoid offending some here, you should simplify your question to the
minimal code needed, including all the type definitions that are not part of
Standard C, like this:

struct foo {
int a,b,c,d;
int e;
}

struct bar {
int a,b,c,d;
char e[sizeof(int)];
}

Now, if I understand correctly, your question is what happens when you cast
a (struct foo *) to a (struct bar *).

The short answer is: absolutely nothing at the time of the cast, but
accessing the 'e' member (of the casted form) invokes implementation-defined
behavior. (I think)
Now in gdb, I can do a display of *test and see what's
in the structure. But I do not understand the char[]
conversion.

There is no conversion. In the particular case you cite, there are many
variants of struct sockaddr each with unique contents; the char[] at the end
is padding such that all of the structs have the same size. This allows one
to pass a (struct sockaddr_in *) to a function expecting a (struct sockaddr
*), provided you also pass an idenfier, i.e. AF_INET, which tells the
receiving function how to properly cast the argument back to extract its
contents.

S
 
A

Alan Balmer

(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

It's not really a conversion. Nothing is moved or changed. You are
just pretending that block of storage is now a struct sockaddr, where
before you were pretending it was a struct sockaddr_in. Whatever was
in those storage locations is still there.
 
D

Dan Pop

In said:
tweak said:
I do not see why typecasting structures would be specific to unix.
You should be able to do this across platforms. I am posting here
to find out about the conversion between the two structures. I
do not understand the integer to char[] type conversion happening
when I write:

To avoid offending some here, you should simplify your question to the
minimal code needed, including all the type definitions that are not part of
Standard C, like this:

struct foo {
int a,b,c,d;
int e;
}

struct bar {
int a,b,c,d;
char e[sizeof(int)];
}

Now, if I understand correctly, your question is what happens when you cast
a (struct foo *) to a (struct bar *).

The short answer is: absolutely nothing at the time of the cast, but

This is not neccesarily correct. Casts between different types mean
conversions and there is nothing in the C standard telling us that
pointers to different structures use the same representation.
What is true is that the pointed-to object is not affected in any
way by the cast.
accessing the 'e' member (of the casted form) invokes implementation-defined
behavior. (I think)

It depends on how you access it. Barring a pathological implementation
that inserts arbitrary padding, if you treat the array e as containing
an int, everything is fine. If you look at it on a byte by byte basis,
then it's better to declare it as array of unsigned char. In that case,
you can access the representation of the corresponding field of struct
foo.

Dan
 
D

Dan Pop

In said:
(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

It's not really a conversion. Nothing is moved or changed.

Except for the pointer value itself, that is *converted* from one type to
another. If you claim that this conversion is a no-op, please provide
chapter and verse.

Dan
 
C

Chris Torek

It's not really a conversion. Nothing is moved or changed.

Well, more precisely, there *is* a conversion -- but the conversion
is not the one quoted in ">>" above.
You are just pretending that block of storage is now a struct
sockaddr, where before you were pretending it was a struct
sockaddr_in. Whatever was in those storage locations is still there.

This, of course, is correct.

The result of "&servaddr" -- a value of type "pointer to struct A"
(for some A) -- is converted to a new value of type "pointer to
struct B" (for some B). The data to which the original pointer
points are unchanged, but whether the new pointer is at all useful
is specific to the implementation.

Unix-like systems with "sockaddr"s and "sockaddr_in"s -- let me
call these "POSIX systems" for short, although even that is not
strictly true -- are much more strongly constrained than is "C in
general", so that while much of POSIX can be written using C, there
are machines that can run C that can never run POSIX. This is
similar to the way that all trucks are vehicles, but not all vehicles
are trucks. If you specify "a vehicle that can carry three tons
of cargo", you have ruled out a lot of possible vehicles -- but
now you can be sure that you can do something with the remaining
subset. (If you load three tons of cargo onto a Yugo or a racecar,
what happens?) If you specify "a computer that has POSIX", you have
ruled out a lot of possible computers, but now you can be sure you
can do something -- sockaddr and sockaddr_in, for instance -- with
the remaining subset.

Here in comp.lang.c we try not to get too specific about large-scale
cargo-hauling, in case your interests are Formula 1 racers or
restoring Model Ts. :) We try to talk about things that apply
to *all* C systems, as defined by the C standard.
 
A

Alan Balmer

In said:
(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

It's not really a conversion. Nothing is moved or changed.

Except for the pointer value itself, that is *converted* from one type to
another. If you claim that this conversion is a no-op, please provide
chapter and verse.
He didn't ask about the pointer value, but the contents of the struct.

Go away.
 
T

tweak

Dan said:
I do not see why typecasting structures would be specific to unix.
You should be able to do this across platforms. I am posting here
to find out about the conversion between the two structures. I
do not understand the integer to char[] type conversion happening
when I write:

To avoid offending some here, you should simplify your question to the
minimal code needed, including all the type definitions that are not part of
Standard C, like this:

struct foo {
int a,b,c,d;
int e;
}

struct bar {
int a,b,c,d;
char e[sizeof(int)];
}

Now, if I understand correctly, your question is what happens when you cast
a (struct foo *) to a (struct bar *).

The short answer is: absolutely nothing at the time of the cast, but


This is not neccesarily correct. Casts between different types mean
conversions and there is nothing in the C standard telling us that
pointers to different structures use the same representation.
What is true is that the pointed-to object is not affected in any
way by the cast.

accessing the 'e' member (of the casted form) invokes implementation-defined
behavior. (I think)


It depends on how you access it. Barring a pathological implementation
that inserts arbitrary padding, if you treat the array e as containing
an int, everything is fine. If you look at it on a byte by byte basis,
then it's better to declare it as array of unsigned char. In that case,
you can access the representation of the corresponding field of struct
foo.

Dan

I will now modify my code going forward to simple examples.

Cheers,

Brian
 
T

tweak

Alan said:
(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.


It's not really a conversion. Nothing is moved or changed. You are
just pretending that block of storage is now a struct sockaddr, where
before you were pretending it was a struct sockaddr_in. Whatever was
in those storage locations is still there.

If you assign the cast portion of the function to a pointer of the
typecast and dereference that pointer, the values appear to change.
I will say that convert to something, but don't actually change.

/* create pointer of type cast */

struct sockaddr *test;

/* assign servaddr */

test = (struct sockaddr *) &servaddr

in gdb, look at *test after the assignment.

sa_data looks like "\0\117\r\0 . . . "

I put the actual output in my response to my typecasting
structures thread.

I do not understand sa_data[]. I understand that the last
zeros exist to establish the structure at a specific size,
but I do not understand the items before the '\0' padding.

Any ideas?

Cheers,

Brian
 
B

Barry Schwarz

In said:
(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

It's not really a conversion. Nothing is moved or changed.

Except for the pointer value itself, that is *converted* from one type to
another. If you claim that this conversion is a no-op, please provide
chapter and verse.

The requirement that all pointers to struct have the same
representation would seem to satisfy your request.


<<Remove the del for email>>
 
R

Ralmin

Dan Pop said:
This is not neccesarily correct. Casts between different types
mean conversions and there is nothing in the C standard telling
us that pointers to different structures use the same
representation. What is true is that the pointed-to object is
not affected in any way by the cast.

C99 6.6.2.1 #26 "All pointers to structure types shall have the same
representation and alignment requirements as each other."

The type of the expression changes, but the representation of the pointer
value does not.
 
C

CBFalconer

Barry said:
On 10 Jun 2004 16:12:44 GMT, (e-mail address removed) (Dan Pop) wrote:
.... snip ...

The requirement that all pointers to struct have the same
representation would seem to satisfy your request.

Meaning they fit in the same space. However they obviously need
not have the same value, which is some collection of bits. I see
no prohibition against some variation in some of those bits in
accordance with the actual type. I also see no reason for any
implementation to do so.
 
E

Eric Sosman

Dan said:
(struct sockaddr *) &servaddr

the previously assigned values within servaddr are typecast to struct
sockaddr,
which is defined as:

struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

My confusion is how sa_data[14] becomes populated. I do not
understand the conversion.

It's not really a conversion. Nothing is moved or changed.


Except for the pointer value itself, that is *converted* from one type to
another. If you claim that this conversion is a no-op, please provide
chapter and verse.

ISO/IEC 9899:1999, section 6.2.5, paragraph 26, third sentence.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top