Consider the following struct and function to create the struct:
#define MAX_ELEMS 1000
struct stuff {
double magic;
bool is_valid[MAX_ELEMS];
double values[MAX_ELEMS];
};
struct stuff make_stuff(double magic) {
return (struct stuff){
.magic = magic
};
}
In my case, I need stuff.magic to be intialized to the given value, and the stuff.is_valid array to be zero initialized, but I do not want to initialize stuff.values (as they are guarded by stuff.is_valid and initialized on-demand later).
Can I achieve this with designated initalizers?
I know I can achieve it without, but this is uglier and more error-prone (among other reasons, as I now need to explicitly zero stuff.is_valid, perhaps with a memset).
Any field not specifically initialized will be filled with zeros.
So, no, if you initialize any fields, they all get something (even if its just zeros)
It can only be done by "normal" assignments and memset.
struct stuff make_stuff(double magic)
{
struct stuff s;
memset(s.is_valid, 0, sizeof(s.is_valid));
s.magic = magic;
return s;
}
But if you are looking for the efficiency returning such a large struct makes no sense as it will me twice copied in full as structs are passed by the value
It is better to pass the pointer to the structure.
struct stuff *make_stuff(struct stuff *s, double magic)
{
memset(s -> is_valid, 0, sizeof(s -> is_valid));
s -> magic = magic;
return s;
}
Related
#define LENGTH 6
typedef char data_t[LENGTH];
struct foo {
const data_t data;
...
}
...
void bar(data_t data) {
printf("%.6s\n", data);
struct foo myfoo = {*data};
printf("%.6s\n", foo.data);
}
I'm trying to have this struct which holds directly the data I'm interested in, sizeof(foo) == 6+the rest, not sizeof(foo) == sizeof(void*)+the rest. However I can't find a way to initialize a struct of type foo with a data_t. I think maybe I could remove the const modifier from the field and use memcpy but I like the extra safety and clarity.
I don't get any compile errors but when I run the code I get
123456
1??
so the copy didn't work properly I think.
This is for an arduino (or similar device) so I'm trying to keep it to very portable code.
Is it just not possible ?
EDIT: removing the const modifier on the data_t field doesn't seem to help.
It is possible to do this, for some cost >=0.
typedef struct
{
char c[LENGTH];
} data_t; // this struct is freely copyable
struct foo
{
const data_t data; // but this data member is not
int what;
};
void foo (char* x) {
data_t d; // declare freely copyable struct instance
memcpy(d.c, x, sizeof(d.c)); // memcpy it
struct foo foo = { d, 42 }; // initialise struct instance with const member
...
};
Some compilers (e.g. clang) are even able to optimise away the redundant copying (from x to d.c and then from d to foo.data ⇒ from x straight to foo.data). Others (gcc I'm looking at you) don't seem to be able to achieve this.
If you pass around pointers to data_t rather than straight char pointers, you won't need this additional memcpy step. OTOH in order to access the char array inside foo you need another level of member access (.data.c instead of just .data; this has no runtime cost though).
It's impossible to do it in a standard compliant way.
Due to its being const, const char data[6]; must be initialized to be usable, and it may only be initialized statically (static objects with no initializer get automatically zeroed), with a string literal, or with a brace-enclosed initializer list. You cannot initialize it with a pointer or another array.
If I were you, I would get rid of the const, document that .data shouldn't be changed post-initialization, and then use memcpy to initialize it.
(const on struct members doesn't work very well in my opinion. It effectively prevents you from being able to have initializer functions, and while C++ gets around the problem a little bit by having special language support for its constructor functions, the problem still remains if the const members are arrays).
I have a structure where the first element is tested and dependent on its value the rest of the structure will or will not be read. In the cases where the first element's value dictates that the rest of the structure will not be read, do I have to allocate enough memory for the entire structure or just the first element?
struct element
{
int x;
int y;
};
int foo(struct element* e)
{
if(e->x > 3)
return e->y;
return e->x;
}
in main:
int i = 0;
int z = foo((struct element*)&i);
I assume that if only allocating for the first element is valid, then I will have to be wary of anything that may attempt to copy the structure. i.e. passing the struct to a function.
don't force your information into structs where it's not needed: don't use the struct as the parameter of your function.
either pass the member of your struct to the function or use inheritance:
typedef struct {
int foo;
} BaseA;
typedef struct {
int bar;
} BaseB;
typedef struct {
BaseA a;
BaseB b;
} Derived;
void foo(BaseB* info) { ... }
...
Derived d;
foo(&d.b);
BaseB b;
foo(&b);
if you're just curious (and seriously don't use this): you may.
typedef struct {
int foo, goo, hoo, joo;
} A;
typedef struct {
int unused, goo;
} B;
int foo(A* a) { return a->goo; }
...
B b;
int goo = foo((A*)&b);
In general you'll have to allocate a block of memory at least as many bytes as are required to fully read the accessed member with the largest offset in your structure. In addition when writing to this block you have to make sure to use the same member offsets as in the original structure.
The point being, a structure is only a block of memory with different areas assigned different interpretations (int, char, other structs etc...) and accessing a member of a struct (after reordering and alignment) boils down to simply reading from or writing to a bit of memory.
I do not think the code as given is legitimate. To understand why, consider:
struct CHAR_AND_INT { unsigned char c; int i; }
CHAR_AND_INT *p;
A compiler would be entitled to assume that p->c will be word-aligned and have whatever padding would be necessary for p->i to also be word-aligned. On some processors, writing a byte may be slower than writing a word. For example, a byte-store instruction may require the processor to read a word from memory, update one byte within it, and write the whole thing back, while a word-store instruction could simply store the new data without having to read anything first. A compiler that knew that p->c would be word-aligned and padded could implement p->c = 12; by using a word store to write the value 12. Such behavior wouldn't yield desired results, however, if the byte following p->c wasn't padding but instead held useful data.
While I would not expect a compiler to impose "special" alignment or padding requirements on any part of the structure shown in the original question (beyond those which apply to int) I don't think anything in the standard would forbid a compiler from doing so.
You need to only check that the structure itself is allocated; not the members (in that case at least)
int foo(struct element* e)
{
if ( e != 0) // check that the e pointer is valid
{
if(e->x != 0) // here you only check to see if x is different than zero (values, not pointers)
return e->y;
}
return 0;
}
In you edited change, I think this is poor coding
int i = 0;
int z = foo((struct element*)&i);
In that case, i will be allocation on the stack, so its address is valid; and will be valid in foo; but since you cast it into something different, the members will be garbage (at best)
Why do you want to cast an int into a structure?
What is your intent?
I have two structs I'm working with, and they are defined nearly identical. These are defined in header files that I cannot modify.
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
uint32_t property1;
uint32_t property2;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
In my code, I initialize the V2 struct at the top of my file, to cover all my bases:
static const carV2 my_car = {
.property1 = value,
.property2 = value,
/* V2 specific properties */
.property3 = value,
.property4 = value
};
Later, I want to retrieve the values I have initialized and copy them into the struct to be returned from a function via void pointer. I sometimes want V2 properties of the car, and sometimes V1. How can I memcpy safely without having duplicate definitions/initializations? I'm fairly new to C, and its my understanding that this is ugly and engineers to follow me in looking at this code will not approve. What's a clean way to do this?
int get_properties(void *returned_car){
int version = get_version();
switch (version){
case V1:
{
CarV1 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV1)); // is this safe? What's a better way?
}
case V2:
{
CarV2 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV2));
}
}
}
Yes, it's definitely possible to do what you're asking.
You can use a base struct member to implement inheritance, like this:
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
CarV1 base;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
In this case, you're eliminating the duplicate definitions. Of course, on a variable of type CarV2*, you can't reference the fields of the base directly - you'll have to do a small redirection, like this:
cv2p->base.property1 = 0;
To upcast to CarV1*, do this:
CarV1* cv1p = &(cv2p->base);
c1vp->property1 = 0;
You've written memcpy(&car, &my_car, sizeof(CarV1)). This looks like a mistake, because it's copying the data of the pointer variable (that is, the address of your struct, instead of the struct itself). Since car is already a pointer (CarV1*) and I'm assuming that so is my_car, you probably wanted to do this instead:
memcpy(car, my_car, sizeof(CarV1));
If my_car is CarV2* and car is CarV1* (or vice versa), then the above code is guaranteed to work by the C standard, because the first member of a struct is always at a zero offset and, therefore, the memory layout of those two for the first sizeof(CarV1) bytes will be identical.
The compiler is not allowed to align/pad that part differently (which I assume is what you meant about optimizing), because you've explicitly declared the first part of CarV2 to be a CarV1.
Since in your case you are stuck with identically defined structs that you can't change, you may find useful that the C standard defines a macro/special form called offsetof.
To be absolutely sure about your memory layouts, I'd advise that you put a series of checks during the initialization phase of your program that verifies whether the offsetof(struct CarV1, property1) is equal to offsetof(struct CarV2, property1) etc for all common properties:
void validateAlignment(void)
{
if (offsetof(CarV1, property1) != offsetof(CarV2, property1)) exit(-1);
if (offsetof(CarV1, property2) != offsetof(CarV2, property2)) exit(-1);
// and so on
}
This will stop the program for going ahead in case the compiler has done anything creative with the padding.
It also won't slow down your program's initialization because offsetof is actually calculated at compile time. So, with all the optimizations in place, the void validateAlignment(void) function should be optimized out completely (because a static analysis would show that the exit conditions are always false).
What you wrote will almost work, except that instead of memcpy(&car, ... you should just have memcpy (car, ..., but there is no reason to use memcpy in such a case. Rather, you should just copy each of the fields in a separate statement.
car->property1 = my_car.property1
(is my_car a pointer or not? it's impossible to tell from the code fragment)
For the second case, I think you can just assign the entire struct: *car = my_car
there is no perfect solution but one way is to use a union
typedef union car_union
{
CarV1 v1;
CarV2 v2;
} Car;
that way the size will not differ when you do a memcpy - if version v1 then v2 specific parts will not be initialized.
In C and Objective-C, this is fine in practice. (In theory, the compiler must see the declaration of a union containing both structs as members).
In C++ (and Objective-C++), the language very carefully describes when this is safe and when it isn't. For example, if you start with
typedef struct {
public:
...
then the compiler is free to re-arrange where struct members are. If the struct uses no C++ features then you are safe.
I create drvm *drv structure in my function. This structure itself contains fields which contains malloc()-ed fields (uint32_t *buffer). The code which do that is similar to that:
...
size_t elm_size = sizeof(model*);
uint32_t length = *(uint32_t*)len;
GArray *models = g_array_sized_new(FALSE, FALSE, elm_size, length);
model *mod;
for (int i = 0; i < length; ++i) {
mod = create_model(...);
g_array_append_val(models, mod);
}
This piece of code doesn't contain errors and is highly tested.
At the start of program I register function free_all() (by atexit()) which should clean all resources (especially memory) when exit() is performed.
Inside this function I'm trying freeing memory of elements of GArray* (model * structure) and memory for GArray * itself:
GArray *models;
g_array_set_clear_func(models, clean_model);
if(!g_array_free(models, FALSE)) { //OK }
The problem is that when clean_model(void *data) is called inside glib library I suggest it contains pointer to one model * element. But the address is wrong, it doesn't seem point to any correct value. Neither GArray*, nor model*.
Furthermore GArray *models in free_all() function is correct (the same as when I created it) and when I iterate through all GArray * elements in free_all() by
for (int i = 0; i < len; ++i) {
mod = g_array_index(models, model*, i); // Here I get correct pointer to model*
clean_model(mod);
}
I get expected values.
Question: What's wrong? How should I free memory of elements of GArray * if these elements contain malloc()-ed memory?
Part of header:
struct _info {
uint32_t *buffer;
uint32_t len;
};
typedef struct _info info;
struct _prod {
uint32_t *buffer;
uint32_t len;
};
typedef struct _prod prod;
struct _model {
uint32_t name;
prod product;
info inform;
};
typedef struct _model model;
struct _drvm {
GArray *models;
GArray *strings;
};
typedef struct _drvm drvm;
Basically the problem is that your clean_model function is passed model** instead of model* you were expecting.
Remember that GArray is meant to store complete structs, not just pointers to structs. In order to do that it needs to copy the whole contents of the struct into the internal data array and therefore any subsequent pointers to the structs (as passed to clean_model) are going to be pointing somewhere inside data (i.e. clean_model((elt_type*)&models->data[index * sizeof(elt_type)]) - where in your case elt_type is model*)
To fix the situation couple options come to mind, in order of (subjective) preference:
use GPtrArray instead; given that your elements are dynamically allocated already the memory management / pointer handling / typecasts (or lack thereof) would be less confusing
change clean_model argument to model**
use GArray to store model structs rather than pointers, but only makes sense if you can separate the allocation from populating the model contents, e.g. g_array_new(FALSE, FALSE, sizeof(model)) and fill_model(&g_array_index(models, model, i))
In all cases you should also probably pass TRUE to g_array_free since you don't seem to be using the GArray.data for anything afterwards (not that it would make any sense given that you're freeing all the useful data in it anyway.)
I can understand how a void** might look in memory, but I'm wondering if I'm using it quite right. Are there any fundamental flaws in what I describe below? For example, although I can say "it works for me", am I creating bad / unportable code in some way?
So I have an Asteroids clone. There are three entities that can fire bullets, the players (SHIP *player_1, SHIP *player_2) and the UFO (UFO *ufo). When a bullet is fired, it's important to know who fired the bullet; if it was a player, when it hits something their score needs to be incremented. So, the bullet will store what kind of entity it belongs to (owner_type) and also a pointer directly to the owner (owner):
enum ShipType
{
SHIP_PLAYER,
SHIP_UFO
};
typedef struct Bullet
{
// ...other properties
enum ShipType owner_type;
void **owner;
} BULLET;
Then, when the player hits the button or the UFO sees a target, one of these functions will be called:
void ship_fire(SHIP **shipp)
{
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner_type = SHIP_PLAYER;
bullet->owner = (void**)shipp;
// do other things
}
void ufo_fire(UFO **ufop)
{
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner_type = SHIP_UFO;
bullet->owner = (void**)ufop;
// do other things
}
... they may be called, for example, like this:
ship_fire(&player_1);
Finally, when the bullet hits a target (such as an asteroid), we dereference the owner. If it's a ship, we can increment the score there and then.
void hit_asteroid(ASTEROID *ast, BULLET *bullet)
{
SHIP *ship_owner;
if (bullet->owner_type == SHIP_PLAYER && *bullet->owner != NULL)
{
ship_owner = (SHIP*)*bullet->owner;
ship_owner->score += 1000;
}
}
Does that seem a reasonable approach? Like I say, it works for me, but I only have a couple of months of C experience.
A final note: why do I not use a void* instead of a void**? Because I want to avoid dangling pointers. In other words, say that player_1 dies and is free'd, but their bullet keeps going and hits an asteroid. If I only have a void*, the hit_asteroid function has no way of knowing that bullet->owner points to de-allocated memory. But with a void**, I can validly check to see if it's NULL; if player_1 is NULL, then *bullet->owner will be NULL too.
EDIT: All respondents so far concur that using a void** probably isn't necessary here because I can avoid the dangling pointers issue (by just statically allocating the base object, for instance). They're correct and I will refactor. But I'm still kinda interested to know if I've used void** in a way that might break something e.g. in terms of memory allocation / casting. But I guess if no-one has thrown their hands in the air and declared it faulty, it at least resembles something that would technically work.
Thanks!
Even if you wanted to continue doing it the way you were, you don't need to use void ** (and shouldn't).
Although void * is a generic pointer type, void ** is not a generic pointer-to-pointer type - it should always point to a genuine void * object. Your code dereferences a SHIP ** or UFO ** pointer through an lvalue of type void ** - that's technically not guaranteed to work. (This happens when you do (SHIP*)*bullet->owner).
However, the good news is that you could continue to use the double-pointer method, using a plain void * to do the job. void * can happily store a pointer-to-a-pointer (because that, after all, is just another kind of pointer). If you change owner to void *, then in ship_fire you would do this:
bullet->owner = shipp;
and in hit_asteroid you would do this:
ship_owner = *(SHIP **)bullet->owner;
In general, the rule for working with pointer casts is: First cast the pointer back to the pointer type that you know it really is, then dereference.
The linux kernel does this in an interesting way. It would be something like
/**
* container_of - cast a member of a structure out to the containing structure
* #ptr: the pointer to the member.
* #type: the type of the container struct this is embedded in.
* #member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typedef struct Ship {
void (*fire)(struct Ship * shipp);
/* ...other methods...*/
} SHIP;
#define playership_of(shipp) container_of(shipp, PLAYERSHIP, ship)
#define ufoship_of(shipp) container_of(shipp, UFOSHIP, ship)
typedef struct PlayerShip {
/* PlayerShip specific stuff ...*/
SHIP ship;
/*...*/
} PLAYERSHIP;
typedef struct UFOShip {
/*...UFO stuff...*/
SHIP ship;
/*...*/
} UFOSHIP;
void ship_fire(SHIP * shipp)
{
shipp->fire(shipp);
}
void player_fire(SHIP *shipp)
{
PLAYERSHIP * ps = playership_of(shipp);
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner = shipp;
// do other things
}
void ufo_fire(SHIP * shipp)
{
UFOSHIP * ufos = ufoship_of(shipp);
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner = ufop;
// do other things
}
UFOSHIP ufoship = { /*...*/ .ship = { .fire = ufo_fire } /* ... */ };
PLAYERSHIP playership = { /*...*/ .ship = { .fire = player_fire } /*...*/ };
/* ... */
ship_fire(&playership.ship);
Read the linux kernel source code for lots of examples of this tecnique.
Since you only have two possible types, I'd use a union for this sort of thing, like so:
typedef struct Bullet {
enum ShipType owner_type;
union {
SHIP *ship;
UFO *ufo;
} owner;
} BULLET;
/* and then... */
void hit_asteroid(ASTEROID *ast, BULLET *bullet)
{
SHIP *ship_owner;
if (bullet->owner_type == SHIP_PLAYER && bullet->owner.ship != NULL) {
ship_owner = bullet->owner.ship;
ship_owner->score += 1000;
}
}
Note that I didn't use the pointer-to-a-pointer scheme that you used. I'm not really convinced of the necessity of it, and the code I suggested doesn't require such a technique.
First off, check the union construct suggested by mipadi; that's a very readable and efficient way of dealing with polymorphism.
Closer to your snippet/question, at a quick glance, I don't see the need/use for the double indirection introduced by pointer-to-pointers. The whole logic would work the same if the arguments to xxxx_fire() methods were [direct] pointers to xxxx objects (and if the typecast etc. in the rest of the logic were to follow accordingly.
Pointers to pointers are useful when the value of the intermediate pointer may be changed at some point. For example if the underlying object is moved, or if it replace by a different object altogether (say a better-equipped ship part of a new level in game etc...)
Edit: (on the use of double indirection to manage "fleets" of objects which may be deallocated.
Responding to your comment, do not refactor so that the objects are not de-allocated (from memory) when they get killed/detroyed (as part of the game). Instead, look into something like the following, as this is indeed an example where the pointer-to-pointer construct helps a lot. Here's how it could work:
Upon game (or level) initialization, allocate an array of pointers big enough to contain as many pointers as the total number of objects the game may allocate, over time. Initialize all its values to NULL.
Introduce an int value index, which indicates the location of the next available (=unused so far) pointer in this array.
When a new object (UFO, Ship or what have you) gets created, four things happen:
new memory is allocated for the object per se
the address of this new memory is stored in the object pointer array (at the location indicated by the index)
the index gets incremented
the "world" only knows this object by way of the double indirection
when an object gets destroyed two things happen
the memory is freed
the pointer in the array is set to null
when accessing any objects the program does three things
first dereference (once) the pointer-to-pointer
check if this is null (if so this indicate the object doesn't exist anymore, the logic may decide to remove this reference from wherever it stored it, as so to not try again, but this is of course optional).
access the actual object by dereferencing the intermediate pointer (if it isn't NULL)
In insight, a short snippet in C language may have been more explicit; sorry I described this in words...
If your bullet owners are frequently changed (e.g. deallocated), the pointer-to-pointer approach is suitable. The union solution does not address this concern directly; as presented, it does not support deallocating ships without touching the pointer on each of that ship's bullets. Of course, that may actually be a practical solution in some implementations, e.g. if you have a need to find all the bullets of a given player, you could maintain a linked list of them: a “next_bullet” pointer for each bullet and “last_bullet” pointer to the head of the list for each player.
And instead of allocating each bullet separately, I would also follow mjv's suggestion of pre-allocating some number of them and picking the next available one. In the linked list implementation, you could use the same “next_bullet” pointers to maintain one list of pre-allocated bullets not currently in use. The advantage of this approach is that you could easily allocate more if you ran out of them, instead of maintaining an array, i.e. if the list of available bullets is empty just add them to the list on demand. Similarly, put “expired” (exploded?) bullets back into the list of available ones and the amount allocated will automatically adapt to however many is required.
Another thing that comes to mind is that you might not need to know which particular UFO (or other enemy) owns a given bullet; just have a single pointer (e.g. SHIP **) for the owning player and set it to NULL for all non-player bullets. If this is not suitable, you could also consider storing the type of each owner in the beginning of owner struct itself, e.g.:
enum EntityType { TYPE_PLAYER_SHIP, TYPE_UFO_SHIP, TYPE_BULLET, };
struct GameEntity {
enum EntityType type;
// This struct is not actually used, it just defines the beginning
};
struct Ship {
enum EntityType type; // Set to TYPE_PLAYER_SHIP when allocating!
…
};
struct UFO {
enum EntityType type; // Set to TYPE_UFO_SHIP when allocating!
…
};
struct Bullet {
enum EntityType type; // Set to TYPE_BULLET when allocating!
struct GameEntity *owner;
…
};
struct Bullet *ship_fire (struct Ship *ship) {
Bullet *b = get_next_available_bullet();
b->owner = (struct GameEntity *) ship;
return b;
}
void hit_asteroid (struct Asteroid *ast, struct Bullet *bullet) {
if (bullet->owner && bullet->owner->type == TYPE_PLAYER_SHIP) {
…
}
}
Note that this trick relies on pointers to different types of structs being interchangeable, and the single enum being stored at the same offset in each type of struct. In practice these are not unreasonable assumptions, but I'm not certain that this behaviour is strictly guaranteed in standard C (however, e.g. struct sockaddr uses the same trick, and it's used by various POSIX networking functions like bind).
I would do like this:
enum _ShipType
{
SHIPT_PLAYER,
SHIPT_UFO, //trailing , is good if you need to add types later
};
typedef struct _Bullet
{
// ...other properties
struct _Bullet_Owner
{
enum _ShipType type;
void* ship;
}owner;
} Bullet;
void ship_fire(Player* p)
{
Bullet* b = malloc(sizeof(Bullet));
// ...other init
b->owner.type = SHIPT_PLAYER;
b->owner.ship = p;
}
If there's only <constant> players, you would be better off having a dead flag for each and setting when they die. (And having them statically allocated.)
#define PLF_DEAD 0x1
//more stuff
struct _Player
{
long flags;
//other data;
}player_1,player_2;
Or you could have an array, or...
Edit: Nonconstant players, a horrifically overengineered solution:
typedef struct _PShip
{
long nweakrefs;
void** weakrefs;
//etc...
}PShip;
PShip* PShip_new(/* args or void */)
{
PShip t;
t = malloc(sizeof(PShip));
t->nweakrefs = 1;
t->weakrefs = malloc(sizeof(void*)*t->nweakrefs);
//other stuff
}
void PShip_regref(PShip** ref)
{
void** temp;
temp = realloc((*ref)->weakrefs,(*ref)->nweakrefs);
if(!temp){/* handle error somehow */}
(*ref)->weakrefs = temp;
(*ref)->weakrefs[(*ref)->nweakrefs++] = ref;
}
void PShip_free(PShip* ship)
{
long i;
for(i=0;i<ship->nweakrefs;i++)
{
if(ship->weakrefs[i]){*(ship->weakrefs[i]) = 0;}
}
//other stuff
}
Alternatively, a reference count might work well, without the O(n) memory.
typedef struct _PShip
{
long refs;
//etc...
}PShip;
void Bullet_free(Bullet* bullet)
{
//other stuff
if(bullet->owner.type == SHIPT_PLAYER)
{
if(--(((PShip*)(bullet->owner.ship))->refs) <= 0)
{PShip_free(bullet->owner.ship);}
}
}
Also, neither of these is threadsafe.