Cost of nested dereferencing negligible? - c
I have a family of large recursive functions that operate on trees. These trees are defined using "polymorphism", like so
struct foo {
enum abctype type;
union {
struct a ay;
struct b bee;
struct c cee;
}
};
where struct a... c are all nodes in the tree. Each node in the tree (i.e., any object of type struct a... c), points to an object of struct foo. For example:
struct a {
/* definitions */
/*...*/
struct foo *next;
};
Because of this, the struct dereferences end up getting very nested even though my functions aren't overreaching their purposes.
In this case, the nested dereferences are inevitable. It would be absurd to write cute little wrapper functions just to get rid of a ->. But, I've heard many programmers say that you shouldn't go past 3-4 dereferences or your "algorithm needs fixing".
So, what's the verdict? Does my code need fixing? are nested dereferences inefficient?
Edit:
Here's what my data structure looks like in more depth (they're not trees, as I have been told, unless linked list == tree):
struct a {
/* definitions */
/*...*/
struct foo *longitudinal;
};
struct b {
/* definitions */
/*...*/
struct foo *longitudinal;
struct b *transverse;
};
struct c {
/* definitions */
/*...*/
struct foo *longitudinal;
struct c *transverse;
};
Basically, the data structures are this way because the data they handle is organized this way (in my head). I just don't see a way to convert this to a binary tree.
A single pointer does not make a tree; it makes a list. Trees require at least two pointers. (You can find exceptions described at Wikipedia, but it is unlikely — though not impossible — that you're intending to use such an organization for your tree structure.)
I think your data organization is … well, if it is not wrong, then it is at least sub-optimal. You should almost certainly be using a structure more like:
struct tree
{
struct tree *left;
struct tree *right;
enum abctype type;
union {
struct a aye;
struct b bee;
struct c cee;
};
};
Where each of the single-letter structure types contains only the relevant (variant) data and not any tree-related pointers:
struct a
{
/* definitions */
/* …no struct tree *next; or anything similar… */
};
The tree traversal is now nice and uniform. Compare what used to be necessary with what is now necessary. Given the old struct foo *tp, your original code (probably) needed to do ghastly stuff like:
if (tp->type == TYPE_A)
process_next(tp->ay.next);
else if (tp->type == TYPE_B)
process_next(tp->bee.next);
else if (tp->type == TYPE_C)
process_next(tp->cee.next);
else
…report error…
(and similarly with a prev or left and right pointers, or whatever else you used to create an actual tree structure — though even as a list, this is more than a trifle messy).
With the revised scheme, given a struct tree *tp;, you now just use:
process_tree(tp->left);
process_data(tp);
process_tree(tp->right);
The data handling has to deal with the enumeration and accessing the appropriate portion of the anonymous union. This is much the same as before (except you don't need to futz with the tree structure pointers).
Working code
I observe that since you've not shown data for the structures a, b, and c, I've had to guess at what might be appropriate. If that sort of detail matters to you, it is important that you put that information in the question before people get to answer it. (That means, in part, don't go adding data fields to the structures now — you've already blown the opportunity to specify what is in them.)
This code works, more or less. The memory management doesn't have memory access errors, at least with the test data. The data isn't freed; that's an exercise for you to play with. Not all the error checking that should be there is there; that's another exercise for you. And the testing isn't all that comprehensive — guess what that means?
There could be some confusion about how your data structure is supposed to work. I've interpreted it as:
You can have a (longitudinal) list of items in arbitrary order, of type A, B or C. These are stored via struct foo, complete with the anonymous union.
Items of type B can have a transverse list of more type B items.
Items of type C can have a transverse list of more type C items.
Here's some code that works:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct a
{
char name[20];
};
struct b
{
double x;
double y;
struct b *transverse;
};
struct c
{
int height;
int width;
int depth;
struct c *transverse;
};
enum abctype { TYPE_A, TYPE_B, TYPE_C };
struct foo
{
struct foo *longitudinal;
enum abctype type;
union
{
struct a aye;
struct b bee;
struct c cee;
};
};
static struct foo *add_a_long(struct foo *head, const char *name)
{
struct foo *new_foo = malloc(sizeof(*new_foo));
if (new_foo != 0)
{
strncpy(new_foo->aye.name, name, sizeof(new_foo->aye.name)-1);
new_foo->aye.name[sizeof(new_foo->aye.name)-1] = '\0';
new_foo->type = TYPE_A;
new_foo->longitudinal = head;
}
return new_foo;
}
static struct foo *add_b_long(struct foo *head, double x, double y)
{
struct foo *new_foo = malloc(sizeof(*new_foo));
if (new_foo != 0)
{
new_foo->bee.x = x;
new_foo->bee.y = y;
new_foo->bee.transverse = 0;
new_foo->type = TYPE_B;
new_foo->longitudinal = head;
}
return new_foo;
}
static struct foo *add_c_long(struct foo *head, int height, int width, int depth)
{
struct foo *new_foo = malloc(sizeof(*new_foo));
if (new_foo != 0)
{
new_foo->cee.height = height;
new_foo->cee.width = width;
new_foo->cee.depth = depth;
new_foo->cee.transverse = 0;
new_foo->type = TYPE_C;
new_foo->longitudinal = head;
}
return new_foo;
}
static void add_b_trans(struct b *b, double x, double y)
{
struct b *new_b = malloc(sizeof(*new_b));
if (new_b != 0)
{
new_b->x = x;
new_b->y = y;
new_b->transverse = 0;
while (b->transverse != 0)
b = b->transverse;
b->transverse = new_b;
}
}
static void add_c_trans(struct c *c, int height, int width, int depth)
{
struct c *new_c = malloc(sizeof(*new_c));
if (new_c != 0)
{
new_c->height = height;
new_c->width = width;
new_c->depth = depth;
new_c->transverse = 0;
while (c->transverse != 0)
c = c->transverse;
c->transverse = new_c;
}
}
static void print_foo(const char *tag, const struct foo *head)
{
printf("\n%s:\n", tag);
while (head != 0)
{
switch (head->type)
{
case TYPE_A:
printf("A: %s\n", head->aye.name);
break;
case TYPE_B:
{
const struct b *bp = &head->bee;
printf("B-main: (%f,%f)\n", bp->x, bp->y);
while ((bp = bp->transverse) != 0)
printf("B-trans: (%f,%f)\n", bp->x, bp->y);
}
break;
case TYPE_C:
{
const struct c *cp = &head->cee;
printf("C-main: (%d,%d,%d)\n", cp->height, cp->width, cp->depth);
while ((cp = cp->transverse) != 0)
printf("C-trans: (%d,%d,%d)\n", cp->height, cp->width, cp->depth);
}
break;
}
head = head->longitudinal;
}
}
int main(void)
{
struct foo *head = 0;
head = add_a_long(head, "Caterpillar");
print_foo("1 item", head);
head = add_a_long(head, "Ununtrium");
print_foo("2 items", head);
head = add_b_long(head, 1.00000, 1.00000);
head = add_b_long(head, 3.14159, 2.78128);
print_foo("4 items", head);
assert(head->type == TYPE_B);
add_b_trans(&head->bee, 1.2345, 2.3456);
add_b_trans(&head->bee, 9.8765, 6.5432);
print_foo("4 items, 2 transverse B", head);
head = add_a_long(head, "Ununpentium");
head = add_c_long(head, 3, 4, 5);
head = add_c_long(head, 5, 12, 13);
print_foo("6 items", head);
assert(head->type == TYPE_C);
add_c_trans(&head->cee, 7, 20, 27);
add_c_trans(&head->cee, 9, 35, 36);
head = add_a_long(head, "Ununseptium");
head = add_a_long(head, "Ununoctium");
print_foo("Final", head);
return 0;
}
And this is the sample output I get:
1 item:
A: Caterpillar
2 items:
A: Ununtrium
A: Caterpillar
4 items:
B-main: (3.141590,2.781280)
B-main: (1.000000,1.000000)
A: Ununtrium
A: Caterpillar
4 items, 2 transverse B:
B-main: (3.141590,2.781280)
B-trans: (1.234500,2.345600)
B-trans: (9.876500,6.543200)
B-main: (1.000000,1.000000)
A: Ununtrium
A: Caterpillar
6 items:
C-main: (5,12,13)
C-main: (3,4,5)
A: Ununpentium
B-main: (3.141590,2.781280)
B-trans: (1.234500,2.345600)
B-trans: (9.876500,6.543200)
B-main: (1.000000,1.000000)
A: Ununtrium
A: Caterpillar
Final:
A: Ununoctium
A: Ununseptium
C-main: (5,12,13)
C-trans: (7,20,27)
C-trans: (9,35,36)
C-main: (3,4,5)
A: Ununpentium
B-main: (3.141590,2.781280)
B-trans: (1.234500,2.345600)
B-trans: (9.876500,6.543200)
B-main: (1.000000,1.000000)
A: Ununtrium
A: Caterpillar
The canonical way to go about this is to really use the structs like derived classes. I. e.:
struct base {
enum abctype type;
};
struct a {
struct base super;
//whatever other data members `a` happens to have
};
With this approach, you write functions taking a struct base*, which is subsequently cast to one of the subclasses once. Further manipulation of the object uses the derived class pointer with only a single ->.
Btw: If you include a function pointer within struct base, you can directly call the derived class's function (no switch required). Bonus points for grouping the function pointers in a struct of their own (instanciated as global tables), with a single pointer in struct base pointing to the correct function table. That would very, very close to what C++ does under the hood...
struct base_vtable {
void (*foo)(int, double);
int (*bar)(struct base*);
int (*baz)();
};
struct a_vtable {
struct base_vtable super;
double (*bim)();
dobule (*bam)();
};
struct base {
struct base_vtable vtable;
};
struct a {
struct base super;
//whatever
};
And then, somewhere in a .c file:
static struct a_vtable g_a_vtable = {
.super.foo = &a_foo,
.super.bar = &a_bar,
.super.baz = &a_baz,
.bim = a_bim,
.bam = a_bam
};
struct a* a_create(...) {
struct a* me = malloc(sizeof(*me));
me->super->vtable = g_a_vtable;
//further initialization
};
Related
Simple Struct definition in C for Red Black Tree
I don't use the C language since years and now I need it again. I'm trying to build a Red-Black Tree but I'm stuck at the beginning because I'm missing something about "structs". Take a look to my "structs" declarations please, they are easy. This is a header file included in Red_black_tree.c #define BLACK 0 //defalut color #define RED 1 // struct Node { //create a black node by default, to have a red one look at "create_red_node" struct Node *left = NULL; struct Node *right= NULL; int key = 0; int value = 0; char color = BLACK; }; struct Root_t { struct Node* Root; }; struct Node* create_node () { struct Node* black = (Node*) malloc (sizeof(Node)); return black; } struct Node* create_red_node () { struct Node* red = create_node (); red->color=RED; return red; } Root_t* create_tree () { struct Root_t* fake=(Root_t*) malloc (sizeof(Root_t)); struct fake->Root->left=create_red_node (); struct fake->Root->right=create_red_node (); return fake; } I compiled with "gcc Red_black_tree.c -o RBTree". GCC says something like : "expected ';' at end of declaration list" 7 times, or "must use 'struct' tag to refer to type... ", "expected identifier...". What do you think, is it good to create RBTree?
In C, you can not assign while you define the members of a struct Instead of struct T { int a = 1; int b = 2; }; you need something like struct T { int a; int b; }; ... struct T t = {.a = 1, .b = 2}; In addition, all your members are set to 0 (NULL is an alias of (void *)0), when you want to allocate memory and initialize all to zero at the same time you can use calloc struct Node* black = calloc(1, sizeof(*black)); // Dont cast malloc and friends And here: struct fake->Root->left=create_red_node(); , you don't want the struct keyword, instead: fake->Root->left=create_red_node(); Another suggestion: do not hardcode the data of the red-black-tree in the structure, (int key, value;) even if it works you end up with a non reusable container, instead, use a generic pointer to void (void *data;) and a callback to your comparison functions in the implementation. struct Node *insert(struct Node *root, int (*comparer)(const void *, const void *)) ...
Confused about updating a struct member in C
It has been a LONG time (25y) since I have done C and so I forget some things so please forgive the question. Given that I have the following declarations: typedef struct item { int field; } Item; typedef struct data { Item b; } Data; I have been trying to update the struct when its passed to a function and this doesn't work at all. static void foo(Data *data) { data->b.field = 3; // doesn't work, the struct remains unchanged. } static void test() { Data v = {.b = {.field = 2}}; foo(&v); } However, if I alter the declaration slightly, use malloc to allocate it it works. typedef struct data { Item *b; }; static void foo(struct data *data) { data->b->field = 3; // works. } static void test() { Data v = (struct data*) malloc(sizeof(Data)); Item i = (struct item*) malloc(sizeof(Item)); foo(v); free(i); free(v); } Can someone inform me why this is? Is it not possible to have struct members that are updatable as members? How could I make the first example work? Thanks in advance.
Your first approach actually works (and I would have been surprised if it did not): struct item { int field; }; struct data { struct item b; }; static void foo(struct data *data) { data->b.field = 3; } static void test() { struct data v = {.b = {.field = 2}}; printf("v.b.field before calling foo: %d\n", v.b.field); foo(&v); printf("v.b.field afterwards: %d\n", v.b.field); } int main() { test(); } Output: v.b.field before calling foo: 2 v.b.field afterwards: 3 Probably your setting is a different one that than you've shown in the code. Mysterious things (i.e. undefined behaviour) often happens if you access an object after it's lifetime has ended. malloc often prevents such issues as it keeps an object alive until it is explicitly freed. But in your case, there should not be any difference. BTW: the typedef does not make sense, as you do not define an alias for the struct-type just declared. So struct item { int field; }; is sufficient.
C - emulate 'mutable' from C++
I have a C structure like this: struct my_struct { int i; double d; struct expensive_type * t; }; An instance of this structure is created and initialized as: struct my_struct * my_new( int i , double d) { struct my_struct * s = malloc( sizeof * s); s->i = i; s->d = d; s->t = NULL; return s; } Calculating the struct expensive_type * t member is quite expensive, and might not be needed - it is therefor just initialized to NULL - and later calculated on demand: const struct expensive_type * my_get_expensive( const struct my_struct * s) { if (!s->t) s->t = my_expensive_alloc( s->i , s->d ); return s->t; } In C++ I would have used mutable on the struct expensive_type *member, is it possible to achieve something similar in C, i.e. casting away the const locally: { struct my_struct * mutable_s = (struct my_struct*) s; mutable_s->t = ...; } Or is removing const in the signature my only standard-compliant alternative?
You could(1) restructure your code and add a layer of indirection: struct expensive; // Forward declaration, ignore // One could also use a struct expensive * (a pointer) instead // of this structure. IMO giving it a name is the better option. struct expensive_handle { struct expensive * target; }; // Store the simple data members as usual, store a pointer to a // handle (pointer) to the expensive ones struct my_struct { int simple; struct expensive_handle * handle; }; struct expensive { int content; // whatever }; Creating a my_struct must create the additional pointer/handle used for the indirection: struct my_struct * new() { struct my_struct * data = malloc(sizeof(*data)); // Error handling please // Set simple data members data->handle = malloc(sizeof(*(data->handle))); // Error handling please data->handle->target = NULL; return data; } The target member (which will point to the expensive data once it is computed) is set to NULL initially. Accessing (and thus possibly lazy computation of) the expensive data members is then possible even with a const qualified my_struct, because no data member of that my_struct is changed: int get_expensive(struct my_struct const * ptr) { if (ptr->handle->target == NULL) { ptr->handle->target = malloc(sizeof(struct expensive)); // Error handling please puts("A hell of a computation just happened!"); ptr->handle->target->content = 42; // WOO } return ptr->handle->target->content; } The only thing that changes is the data member of *(ptr->handle), a struct expensive_handle. Which is not const qualified (only the pointer to it named handle is). Test (Live on ideone): int main(void) { struct my_struct * p = new(); printf("%d\n", get_expensive(p)); printf("%d\n", get_expensive(p)); } (1) Whether this is reasonable or a complete waste of resources (both programmer and computation) cannot be decided from your dummy example, though.
How to use struct within an union, within a struct in C?
i am currently having a lot of struggle with a, for me personally, very complex structure struct crypto_tfm { uint32_t crt_flags; union { struct ablkcipher_tfm ablkcipher; struct aead_tfm aead; struct blkcipher_tfm blkcipher; struct cipher_tfm cipher; struct hash_tfm hash; struct compress_tfm compress; struct rng_tfm rng; } crt_u; void (*exit)(struct crypto_tfm *tfm); struct crypto_alg *crt_alg; void *crt_ctx[] CRYPTO_MINALIGN_ATTR; }; I completely have no idea how to use this struct. so basicly i am completely lost with this the function using this expects a struct crypto_tfm *tfm first idea is the following: struct crypto_tfm *new_tfm() { struct crypto_tfm *tfm = malloc(sizeof(struct crypto_tfm)); tfm -> crt_flags = 0; tfm -> crt_u. } but i dont know how to get further, the given structs within the union are also using another structs. kinda too complicated for me right now
This is untested, but should be a good example: struct st_a { int a; }; struct st_b { int b; }; union un_c { struct st_a aa; struct st_b bb; }; struct st_d { int d; union un_c cc; }; int main () { struct st_d *dd = malloc (sizeof (struct st_d)); dd->d = 0; /* The following two lines might (probably are) accessing the same area of memory. */ dd->cc.aa.a = 0; dd->cc.bb.b = 1; }
How to initialize a const variable inside a struct in C?
I write a struct struct Tree{ struct Node *root; struct Node NIL_t; struct Node * const NIL; //sentinel } I want struct Node * const NIL = &NIL_t; I can't initialize it inside the struct. I'm using msvs. I use C, NOT C++. I know I can use initialization list in C++. How to do so in C?
If you are using C99, you can used designated initializers to do this: struct Tree t = { .root = NULL, .NIL = &t.NIL_t }; This only works in C99, though. I've tested this on gcc and it seems to work just fine.
For those seeking a simple example, here it goes: #include <stdio.h> typedef struct { const int a; const int b; } my_t; int main() { my_t s = { .a = 10, .b = 20 }; printf("{ a: %d, b: %d }", s.a, s.b); } Produces the following output: { a: 10, b: 20 }
A structure defines a data template but has no data itself. Since it has no data, there's no way to initialize it. On the other hand, if you want to declare an instance, you can initialize that. struct Tree t = { NULL, NULL, NULL };
Maybe something like this will suffice? struct { struct Node * const NIL; struct Node *root; struct Node NIL_t; } Tree = {&Tree.NIL_t};