This is a follow up to this question.
When explaining my problem, I declared that allocated memory could be reused because it has no declared type, and I was told that it was incorrect C.
Here is a code example illustrating the question:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
struct Elt {
int id;
char name[32];
};
struct Elt2 {
double val;
char name[16];
};
static_assert(sizeof(struct Elt2) < sizeof(struct Elt), "Incorrect sizes");
int main() {
struct Elt actual1 = { 1, "foo"};
struct Elt2 actual2 = {2.0, "bar"};
struct Elt* elt = malloc(sizeof(struct Elt));
memcpy(elt, &actual1, sizeof(*elt)); // populates the allocated memory
printf("elt: %d %s\n", elt->id, elt->name);
struct Elt2 *elt2 = (void *) elt; // declares a new pointer to a shorter type
memcpy(elt2, &actual2, sizeof(*elt2)); // effective type is now struct Elt2
printf("elt2: %g %s\n", elt2->val, elt2->name);
//printf("elt: %d %s\n", elt->id, elt->name); UB: storage now contains an Elt2 object
free(elt); // only legal use for elt
return 0;
}
I believe that 6.5 Expression §6 of draft n1570 allows it:
The effective type of an object for an access to its stored value is the declared type of the
object, if any.87) If a value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of the lvalue becomes the
effective type of the object for that access and for subsequent accesses that do not modify
the stored value. If a value is copied into an object having no declared type using
memcpy or memmove, or is copied as an array of character type, then the effective type
of the modified object for that access and for subsequent accesses that do not modify the
value is the effective type of the object from which the value is copied, if it has one.
with note 87:
87) Allocated objects have no declared type.
Question:
Is reusing allocated memory for storing a different object that can fit in that memory conformant C?
If not, that would be catastrophic. Many people use such tricks to implement their own fine grained memory management on to of malloc.
So, yes, this is exactly what the paragraph in the standard that you are citing is about. Notice that it choses the words carefully. It says
If a value is stored into an object having no declared type ...
this property of having no declared type doesn't change through the lifetime of the object, so the provision applies at any time a new value is written into it.
If, for some weird reason the committee would have wanted to say that the effective type is only changeable once, they would have say something like
If a value is stored into an object having no effective type ...
The only correct answer is one that is fully derived from the standard.
Without going through the standard, I would say, "Yes, your assumption is correct". I say that because without it, it would not be possible to implement your own memory manager. I think not even malloc could be implemented in C without it.
Related
This question already has answers here:
Are C-structs with the same members types guaranteed to have the same layout in memory?
(4 answers)
Closed 1 year ago.
I'm trying to understand the so-called "common initial sequence" rule for C aliasing analysis. This question does not concern C++.
Specifically, according to resources (for example the CPython PEP 3123),
[A] value of a struct type may also be accessed through a pointer to the first field. E.g. if a struct starts with an int, the struct * may also be cast to an int *, allowing to write int values into the first field.
(emphasis mine).
My question can be roughly phrased as "does the ability to access a struct by pointer to first-member-type pierce nested structs?" That is, what happens if access is via a pointer whose pointed-to type (let's say type struct A) isn't exactly the same type as that of the first member (let's say type struct B), but that pointed-to type (struct A) has common first initial sequence with struct B, and the "underlying" access is only done to that common initial sequence?
(I'm chiefly interested in structs, but I can imagine this question may also pertain to unions, although I imagine unions come with their own tricky bits w.r.t. aliasing.)
This phrasing may not clear, so I tried to illustrate my intention with the code as follows (also available at godbolt.org, and the code seem to compile just fine with the intended effect):
/* Base object as first member of extension types. */
struct base {
unsigned int flags;
};
/* Types extending the "base" by including it as first member */
struct file_object {
struct base attr;
int index;
unsigned int size;
};
struct socket_object {
struct base attr;
int id;
int type;
int status;
};
/* Another base-type with an additional member, but the first member is
* compatible with that of "struct base" */
struct extended_base {
unsigned int flags;
unsigned int mode;
};
/* A type that derives from extended_base */
struct extended_socket_object {
struct extended_base e_attr; /* Using "extended" base here */
int e_id;
int e_type;
int e_status;
int some_other_field;
};
/* Function intended for structs "deriving from struct base" */
unsigned int set_flag(struct base *objattr, unsigned int flag)
{
objattr->flags |= flag;
return objattr->flags;
}
extern struct file_object *file;
extern struct socket_object *sock;
extern struct extended_socket_object *esock;
void access_files(void)
{
/* Cast to pointer-to-first-member-type and use it */
set_flag((struct base *)file, 1);
set_flag((struct base *)sock, 1);
/* Question: is the following access defined?
* Notice that it's cast to (struct base *), rather than
* (struct extended_base *), although the two structs share the same common
* initial member and it is this member that's actually accessed. */
set_flag((struct base *)esock, 1);
return;
}
This is not safe as you're attempting to access an object of type struct extended_base as though it were an object of type struct base.
However, there are rules that allow access to two structures initial common sequence via a union. From section 6.5.2.3p6 of the C standard:
One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members
So if you change the definition of struct extended_socket_object to this:
struct extended_socket_object {
union u_base {
struct base b_attr;
struct extended_base e_attr;
};
int e_id;
int e_type;
int e_status;
int some_other_field;
};
Then a struct extended_socket_object * may be converted to union u_base * which may in turn be converted to a struct base *. This is allowed as per section 6.7.2.1 p15 and p16:
15 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase
in the order in which they are declared. A pointer to a structure
object, suitably converted, points to its initial member (or
if that member is a bit-field, then to the unit in which it
resides), and vice versa. There may be unnamed padding within
a structure object, but not at its beginning.
16 The size of a union is sufficient to contain the largest of its members. The value of at most one of the
members can be stored in a union object at any time. A
pointer to a union object, suitably converted, points to each
of its members (or if a member is a bit-field, then to the
unit in which it resides), and vice versa.
It is then allowed to access b_attr->flags because of the union it resides in via 6.5.2.3p6.
According to the C Standard (6.7.2.1 Structure and union specifiers, paragraph 13):
A pointer to a structure object, suitably converted, points to its
initial member (or if that member is a bit-field, then to the unit in
which it resides), and vice versa.
So, converting esock to struct extended_base * and then converting it to unsigned int * must give us a pointer to the flags field, according to the Standard.
I'm not sure if converting to to struct base * counts as "suitably converted" or not. My guess is that it would work at any machine you will try it on, but I wouldn't recommend it.
I think it would be safest (and also make the code more clear) if you simply keep a member of type struct base inside struct extended_base (instead of the member of type unsigned int). After doing that, you have two options:
When you want to send it to a function, write explicitly: esock->e_attr.base (instead of (struct base *)esock). This is what I would recommend.
You can also write: (struct base *) (struct extended_base *) esock which is guaranteed to work, but I think it is less clear, and also more dangerous (if in the future you will want to add or accidentaly add another member in the beginning of the struct).
After reading up into the standard's text following the other answers (thanks!!) I think I may try to answer my own question (which was a bit misleading to begin with, see below)
As the other answers pointed out, there appear to be two somewhat overlapping concerns in this question -
"common initial sequence" -- in the standard documents this specifically refers to the context of a union having several structs as member and when these member structs share some compatible members beginning from the first. (§6.5.2.3 " Structure and union members", p6 -- Thanks, #dbush!).
My reading: the language spec suggests that, if at the site of access to these "apparently" different structs it is made clear that they actually belong to the same union, and that the access is done through the union, it is permitted; otherwise, it is not.
I think the requirement is meant to work with type-based aliasing rules: if these structs do indeed alias each other, this fact must be made clear at compile time (by involving the union). When the compiler sees pointers to different types of structs, it can't, in the most general case, deduce whether they may have belonged to some union somewhere. In that case, if it invokes type-based alias analysis, the code will be miscompiled. So the standard requires that the union is made visible.
"a pointer (to struct), when suitably converted, points to its initial member" (§6.7.2.1 "Structure and union specifiers", p15) -- this sounds tantalizingly close to 1., but it's less about aliasing than about a) the implementation requirements for struct and b) "suitable conversion" of pointers. (Thanks, #Orielno!)
My reading: the "suitable conversion" appears to mean "see everything else in the standard", that is, no matter if the "conversion" is performed by type cast or assignment (or a series of them), being "suitable" suggests "all constraints must be satisfied at all steps". The "initial-member" rule, I think, simply says that the actual location of the struct is exactly the same as the initial member: there cannot be padding in front of the first member (this is explicitly stated in the same paragraph).
But no matter how we make use of this fact to convert pointers, the code must still be subject to constraints governing conversion, because a pointer is not just a machine representation of some location -- its value still has to be correctly interpreted in the context of types. A counterexample would be a conversion involving an assignment that discards const from the pointed-to type: this violates a constraint and cannot be suitable.
The somewhat misleading thing in my original post was to suggest that rule 2 had something to do with "common initial sequence", where it is not directly related to that concept.
So for my own question, I tend to answer, to my own surprise, "yes, it is valid". The reason is that the pointer conversion by cast in expression (struct base *)esock is "legal in the letter of the law" -- the standard simply says that (§6.5.4 "Cast operators", p3)
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1 (note: constraints governing simple assignment), shall be specified by means of an explicit cast.
Since the expression is indeed an explicit cast, in and by itself it doesn't contradict the standard. The "conversion" is "suitable". Further function call to set_flag() correctly dereferences the pointer by virtue of the suitable conversion.
But! Indeed the "common initial sequence" becomes important when we want to improve the code. For example, in #dbush's answer, if we want to "inherit from multiple bases" via union, we must make sure that access to base is done where it's apparent that the struct is a member of the union. Also, as #Orielno pointed out, when the code makes us worry about its validity, perhaps switching to an explicitly safe alternative is better even if the code is valid in the first place.
In the language the C Standard was written to describe, an lvalue of the form ptr->memberName would use ptr's type to select a namespace in which to look up memberName, add the offset of that member to the address in ptr, and then access an object of that member type at that address. Once the address and type of the member were determined, the original structure object would play no further rule in the processing of the expression.
When C99 was being written, there was a desire to avoid requiring that a compiler given something like:
struct position {double x,y,z; };
struct velocity {double dx,dy,dz; };
void update_positions(struct positions *pp, struct velocity *vv, int count)
{
for (int i=0; i<count; i++)
{
positions[i].x += vv->dx;
positions[i].y += vv->dy;
positions[i].z += vv->dz;
}
}
must allow for the possibility that a write to e.g. positions[i].y might affect the object of vv->dy even when there is no evidence of any relationship between any object of type struct position and any object of type struct velocity. The Committee agreed that compilers shouldn't be required to accommodate interactions between different structure types in such cases.
I don't think anyone would have seriously disputed the notion that in situations where storage is accessed using a pointer which is freshly and visibly converted from one structure type to another, a quality compiler should accommodate the possibility that the operation might access a structure of the original type. The question of exactly when an implementation would accommodate such possibilities should depend upon what its customers were expecting to do, and was thus left as a quality-of-implementation issue outside the Standard's jurisdiction. The Standard wouldn't forbid implementations from being willfully blind to even the most obvious cases, but that's because the dumber something would be, the less need there should be to prohibit it.
Unfortunately, the authors of clang and gcc have misinterpreted the Standard's failure to forbid them from being obtusely blind to the possibility that a freshly-type-converted pointer might be used to access the same object as a pointer of the original type, as an invitation to behave in such fashion. When using clang or gcc to process any code which would need to make use of the Common Initial Sequence guarantees, one must use -fno-strict-aliasing. When using optimization without that flag, both clang nor gcc are prone to behave in ways inconsistent with any plausible interpretation of the Standard's intent. Whether one views such behaviors as being a result of a really weird interpretation of the Standard, or simply as bugs, I see no reason to expect that gcc or clang will ever behave meaningfully in such cases.
Now I know I can implement inheritance by casting the pointer to a struct to the type of the first member of this struct.
However, purely as a learning experience, I started wondering whether it is possible to implement inheritance in a slightly different way.
Is this code legal?
#include <stdio.h>
#include <stdlib.h>
struct base
{
double some;
char space_for_subclasses[];
};
struct derived
{
double some;
int value;
};
int main(void) {
struct base *b = malloc(sizeof(struct derived));
b->some = 123.456;
struct derived *d = (struct derived*)(b);
d->value = 4;
struct base *bb = (struct base*)(d);
printf("%f\t%f\t%d\n", d->some, bb->some, d->value);
return 0;
}
This code seems to produce desired results , but as we know this is far from proving it is not UB.
The reason I suspect that such a code might be legal is that I can not see any alignment issues that could arise here. But of course this is far from knowing no such issues arise and even if there are indeed no alignment issues the code might still be UB for any other reason.
Is the above code valid?
If it's not, is there any way to make it valid?
Is char space_for_subclasses[]; necessary? Having removed this line the code still seems to be behaving itself
As I read the standard, chapter §6.2.6.1/P5,
Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. [...]
So, as long as space_for_subclasses is a char (array-decays-to-pointer) member and you use it to read the value, you should be OK.
That said, to answer
Is char space_for_subclasses[]; necessary?
Yes, it is.
Quoting §6.7.2.1/P18,
As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. In most situations,
the flexible array member is ignored. In particular, the size of the structure is as if the
flexible array member were omitted except that it may have more trailing padding than
the omission would imply. However, when a . (or ->) operator has a left operand that is
(a pointer to) a structure with a flexible array member and the right operand names that
member, it behaves as if that member were replaced with the longest array (with the same
element type) that would not make the structure larger than the object being accessed; the
offset of the array shall remain that of the flexible array member, even if this would differ
from that of the replacement array. If this array would have no elements, it behaves as if
it had one element but the behavior is undefined if any attempt is made to access that
element or to generate a pointer one past it.
Remove that and you'd be accessing invalid memory, causing undefined behavior. However, in your case (the second snippet), you're not accessing value anyway, so that is not going to be an issue here.
This is more-or-less the same poor man's inheritance used by struct sockaddr, and it is not reliable with the current generation of compilers. The easiest way to demonstrate a problem is like this:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
struct base
{
double some;
char space_for_subclasses[];
};
struct derived
{
double some;
int value;
};
double test(struct base *a, struct derived *b)
{
a->some = 1.0;
b->some = 2.0;
return a->some;
}
int main(void)
{
void *block = malloc(sizeof(struct derived));
if (!block) {
perror("malloc");
return 1;
}
double x = test(block, block);
printf("x=%g some=%g\n", x, *(double *)block);
return 0;
}
If a->some and b->some were allowed by the letter of the standard to be the same object, this program would be required to print x=2.0 some=2.0, but with some compilers and under some conditions (it won't happen at all optimization levels, and you may have to move test to its own file) it will print x=1.0 some=2.0 instead.
Whether the letter of the standard does allow a->some and b->some to be the same object is disputed. See http://blog.regehr.org/archives/1466 and the paper it links to.
I am having a little bit of confusion about derefrencing a structure pointer to a
structure variable.
It will be good if I demonstrate my problem with an example.
So here I am:
struct my_struct{
int num1;
int num2;
}tmp_struct;
void Display_struct(void * dest_var){
struct my_struct struct_ptr;
struct_ptr = *((struct my_struct *)dest_var);
printf("%d\t%d\n",struct_ptr.num1,struct_ptr.num2);
}
int main()
{
tmp_struct.num1 = 100;
tmp_struct.num2 = 150;
Display_struct(&tmp_struct);
return 0;
}
Now when I am running this example I am able to get the code to be compiled in a very clean manner and also the output is correct.
But what I am not able to get is that is this a correct way of dereferencing the structure pointer to a structure variable as we do in case of other simple
data types like this:
int example_num;
void Display_struct(void * dest_var){
int example_num_ptr;
example_num_ptr = *((int *)dest_var);
printf("%d\t%d\n",struct_ptr.num1,struct_ptr.num2);
}
int main()
{
example_num = 100;
Display_struct(&example_num);
return 0;
}
Here we can dereference the int pointer to int variable as it is a simple data
type but in my opinion we can't just dereference the structure pointer in similar manner to a structure variable as it is not simple data type but a complex data type or data structure.
Please help me in resolving the concept behind this.
The only problem is that you have to guarantee that the passed void* points to a variable of the correct struct type. As long as it does, everything will work fine.
The question is why you would use a void pointer and not the expected struct, but I assume this function is part of some generic programming setup, otherwise it wouldn't make sense to use void pointers.
However, if you would attempt something "hackish" like this:
int arr[2] = {100, 150};
Display_struct(arr); // BAD
Then there are no longer any guarantees: the above code will compile just fine but it invokes undefined behavior and therefore may crash & burn. The struct may contain padding bytes at any place and the code also breaks the "strict aliasing" rules of C.
(Aliasing refers to the rules stated by the C standard chapter 6.5 Expressions, 7§)
You are thinking up a problem where there isn't any. A struct-type (alias an aggregate data type) is technically not very different from any other type.
If we look at things on the lower level, a variable of any type (including a struct type) is just some number of bits in memory.
The type determines the number of bits in a variable and their interpretation.
Effectively, whether you dereference a pointer-to-int or a pointer-to-struct, you just get the chunk of bits your pointer points to.
In your main function, you have struct tmp_struct. It is not a pointer. But it is fine, because you pass address of tmp_struct to the function void Display_struct(void * dest_var).
Then function take the input argument, your pointer(void*). It hold the address of 'tmp_struct`.
Then inside the function you are de-referencing correctly.
struct_ptr = *((struct my_struct *)dest_var);
you deference void* to struct my_struct type. Your de-referencing correct, because you pass same type object. Otherwise it will cause run time issues.
No matter how complex your data type or data structure, de-referencing should work fine.
But if input arg type is void* make sure to pass struct my_struct to function.
Why does this work:
#include <sys/types.h>
#include <stdio.h>
#include <stddef.h>
typedef struct x {
int a;
int b[128];
} x_t;
int function(int i)
{
size_t a;
a = offsetof(x_t, b[i]);
return a;
}
int main(int argc, char **argv)
{
printf("%d\n", function(atoi(argv[1])));
}
If I remember the definition of offsetof correctly, it's a compile time construct. Using 'i' as the array index results in a non-constant expression. I don't understand how the compiler can evaluate the expression at compile time.
Why isn't this flagged as an error?
The C standard does not require this to work, but it likely works in some C implementations because offsetof(type, member) expands to something like:
type t; // Declare an object of type "type".
char *start = (char *) &t; // Find starting address of object.
char *p = (char *) &t->member; // Find address of member.
p - start; // Evaluate offset from start to member.
I have separated the above into parts to display the essential logic. The actual implementation of offsetof would be different, possibly using implementation-dependent features, but the core idea is that the address of a fictitious or temporary object would be subtracted from the address of the member within the object, and this results in the offset. It is designed to work for members but, as an unintended effect, it also works (in some C implementations) for elements of arrays in structures.
It works for these elements simply because the construction used to find the address of a member also works to find the address of an element of an array member, and the subtraction of the pointers works in a natural way.
it's a compile time construct
AFAICS, there are no such constraints. All the standard says is:
[C99, 7.17]:
The macro...
offsetof(type, member-designator)
...
The type and member designator shall be such that given
static type t;
then the expression &(t.member-designator) evaluates to an address constant.
offsetof (type,member)
Return member offset: This macro with functional form returns the offset value in bytes of member member in the data structure or union type type.
http://www.cplusplus.com/reference/cstddef/offsetof/
(C, C++98 and C++11 standards)
I think I understand this now.
The offsetof() macro does not evaluate to a constant, it evaluates to a run-time expression that returns the offset. Thus as long as type.member is valid syntax, the compiler doesn't care what it is. You can use arbitrary expressions for the array index. I had thought it was like sizeof and had to be constant at compile time.
There has been some confusion on what exactly is permitted as a member-designator. Here are two papers I am aware of:
DR 496
Offsetof for Pointers to Members
However, even quite old versions of GCC, clang, and ICC support calculating array elements with dynamic offset. Based on Raymond's blog I guess that MSVC has long supported it too.
I believe it is based out of pragmatism. For those not familiar, the "struct hack" and flexible array members use variable-length data in the last member of a struct:
struct string {
size_t size;
const char data[];
};
This type is often allocated with something like this:
string *string_alloc(size_t size) {
string *s = malloc(offsetof(string, data[size]));
s->size = size;
return s;
}
Admittedly, this latter part is just a theory. It's such a useful optimization that I imagine that initially it was permitted on purpose for such cases, or it was accidentally supported and then found to be useful for exactly such cases.
NOTE: this is NOT a C++ question, i can't use a C++ compiler, only a C99.
Is this valid(and acceptable, beautiful) code?
typedef struct sA{
int a;
} A;
typedef struct aB{
struct sA a;
int b;
} B;
A aaa;
B bbb;
void init(){
bbb.b=10;
bbb.a.a=20;
set((A*)&bbb);
}
void set(A* a){
aaa=*a;
}
void useLikeB(){
printf("B.b = %d", ((B*)&aaa)->b);
}
In short, is valid to cast a "sub class" to "super class" and after recast "super class" to "sub class" when i need specified behavior of it?
Thanks
First of all, the C99 standard permits you to cast any struct pointer to a pointer to its first member, and the other way (6.7.2.1 Structure and union specifiers):
13 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
In other way, in your code you are free to:
Convert B* to A* — and it will always work correctly,
Convert A* to B* — but if it doesn't actually point to B, you're going to get random failures accessing further members,
Assign the structure pointed through A* to A — but if the pointer was converted from B*, only the common members will be assigned and the remaining members of B will be ignored,
Assign the structure pointed through B* to A — but you have to convert the pointer first, and note (3).
So, your example is almost correct. But useLikeB() won't work correctly since aaa is a struct of type A which you assigned like stated in point (4). This has two results:
The non-common B members won't be actually copied to aaa (as stated in (3)),
Your program will fail randomly trying to access A like B which it isn't (you're accessing a member which is not there, as stated in (2)).
To explain that in a more practical way, when you declare A compiler reserves the amount of memory necessary to hold all members of A. B has more members, and thus requires more memory. As A is a regular variable, it can't change its size during run-time and thus can't hold the remaining members of B.
And as a note, by (1) you can practically take a pointer to the member instead of converting the pointer which is nicer, and it will allow you to access any member, not only the first one. But note that in this case, the opposite won't work anymore!
I think this is quite dirty and relatively hazardous. What are you trying to achieve with this? also there is no guarantee that aaa is a B , it might also be an A. so when someone calls "uselikeB" it might fail. Also depending on architecture "int a" and "pointer to struct a" might either overlap correctly or not and might result in interesting stuff happening when you assign to "int a" and then access "struct a"
Why would you do this? Having
set((A*)&bbb);
is not easier to write than the correct
set(&bbb.a);
Other things that you should please avoid when you post here:
you use set before it is declared
aaa=a should be aaa = *a
First of all, I agree with most concerns from previous posters about the safety of this assignments.
With that said, if you need to go that route, I'd add one level of indirection and some type-safety checkers.
static const int struct_a_id = 1;
static const int struct_b_id = 2;
struct MyStructPtr {
int type;
union {
A* ptra;
B* ptrb;
//continue if you have more types.
}
};
The idea is that you manage your pointers by passing them through a struct that contains some "type" information. You can build a tree of classes on the side that describe your class tree (note that given the restrictions for safely casting, this CAN be represented using a tree) and be able to answer questions to ensure you are correctly casting structures up and down. So your "useLikeB" function could be written like this.
MyStructPtr the_ptr;
void init_ptr(A* pa)
{
the_ptr.type = struct_a_id
the_ptr.ptra = pa;
}
void useLikeB(){
//This function should FAIL IF aaa CANT BE SAFELY CASTED TO B
//by checking in your type tree that the a type is below the
//a type (not necesarily a direct children).
assert( is_castable_to(the_ptr.type,struct_b_id ) );
printf("B.b = %d", the_ptr.ptrb->b);
}
My 2 cents.