I have only been learning C for less than a week (with my knowledge of C++ and other languages to help) and I am confused on pointers and their ways of being declared.
Below, I use a simple struct named Object:
struct Object { int id; };
Do the below methods for creating a pointer do the same thing just in a different way, or no?
struct Object obj1 = { .id = 1 };
struct Object* obj1_p = &obj1; // method 1 of getting a pointer
// The same, just in a compound literal?
struct Object* obj2_p = &(struct Object){ .id = 1 }; // method 2 of getting a pointer
// Is this the same, other than being uninitialized?
struct Object* obj3_p = malloc(sizeof(struct Object)); // method 2 of getting a pointer
Is there a time when you only can use one method?
Also, as a side note, why do some people cast malloc, and is it better to do it?
// malloc is casted to object:
struct Object* obj3_p = (Object*)malloc(sizeof(struct Object));
These two “methods” do exactly the same thing. And as you said, the second one is just a compound literal.
struct Object obj1 = { .id = 1 };
struct Object *obj1_p = &obj1;
// The same, just in a compound literal?
struct Object *obj2_p = &(struct Object){ .id = 1 };
This allocates enough memory for struct Object without initializing it. And no you don't need to cast it, because malloc returns void *, which is automatically and safely promoted to any other pointer. But if you do, you should cast it to struct Object* instead of Object*.
struct Object *obj3_p = (struct Object*) malloc(sizeof(struct Object));
That looks very bulky though... My preferred way of doing it is this:
struct Object *obj3_p = malloc(sizeof *obj3_p);
I wrote this piece of code, hope it helps you to better understand some features of pointers:
#include <stdio.h>
#include <stdlib.h>
struct Object { int id; };
struct Object *getObjectBold() {
struct Object* obj2_p = &(struct Object) { .id = 2 };
return obj2_p; // UB: Returns the address of a local object (the compound literal).
}
struct Object *getObject() {
struct Object* obj3_p = malloc(sizeof(*obj3_p)); // Better way of calling malloc than using sizeof(struct Object).
obj3_p->id = 3; // You don't need to do this.
return obj3_p; // This needs to be freed later on!
}
int main(void) {
struct Object obj1 = { .id = 1 };
struct Object* obj1_p = &obj1;
printf("obj1.id = %d\n", obj1_p->id);
obj1_p->id = 10; // You can change values using the pointer
printf("obj1.id = %d\n", obj1_p->id);
// The only different thing with this case is that you don't
// "lose" your object when setting the pointer to NULL
// (although you can only access it through the object, not through the pointer).
obj1_p = NULL;
printf("obj1.id = %d\n", obj1_p->id); // This won't work (undefined behaviour).
printf("obj1.id = %d\n", obj1.id); // This will.
struct Object* obj2_p = &(struct Object) { .id = 1 };
obj2_p->id = 2; // You can change the id
printf("obj2.id = %d\n", obj2_p->id);
// If you make this pointer point to another address, you "lose" your object.
obj2_p = NULL;
printf("obj2.id = %d", obj2_p->id); // This won't work at all (undefined behaviour).
// Both of these pointers point to objects in the stack, so, for example,
// they don't work when returning from a function.
obj2_p = getObjectBold();
obj2_p->id = 20; // This won't work (undefined behaviour).
printf("obj2.id = %d\n", obj2_p->id); // This works if you don't dereference the pointer.
// The third case is not the same as the other two, since you are allocating memory on the heap.
// THIS is a time where you can only use one of these three methods.
struct Object *obj3_p = getObject(); // This works!
printf("obj3.id = %d\n", obj3_p->id);
obj3_p->id = 30; // This works now.
printf("obj3.id = %d\n", obj3_p->id);
free(obj3_p); // You need to do this if you don't want memory leaks.
return 0;
}
This is the output when commenting out undefined behaviour:
obj1.id = 1
obj1.id = 10
obj1.id = 10
obj2.id = 2
obj2.id = 2
obj3.id = 3
obj3.id = 30
I'd recommend you to check out these links, they turned out to be pretty helpful for me:
Returning a pointer from a function
What and where are the stack and heap?
What EXACTLY is meant by “de-referencing a NULL pointer”?
Why dereferencing a null pointer is undefined behaviour?
Do I cast the result of malloc?
There are two distinct topics in your question.
struct Object* obj1_p = .......;
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^
pointer object initialization
definition
Pointer variable definition
You can define the pointer variable only one way:
type *objectname;
Initialization assigns the value to the pointer variable. This value should reference the valid object of the same type as the pointer or valid memory of the size not smaller than the pointer type. The difference in your examples is how the referenced object is created.
Is there a time when you only can use one method?
That only depends on the program logic. You only must remember about the scope of the underlying object to avoid dereferencing objects which does not exist outside the particular scope:
struct Object *valid1(void) //valid
{
struct Object* obj3_p = malloc(sizeof(*obj3_p));
return obj3_p;
}
struct Object obj1 = { .id = 1 };
struct Object *valid2(void) // valid
{
struct Object* obj3_p = &obj1;
return obj3_p;
}
struct Object *invalid1(void) // invalid
{
struct Object obj1 = { .id = 1 };
struct Object* obj3_p = &obj1;
return obj3_p;
}
struct Object *invalid2(void) // invalid
{
struct Object* obj3_p = &(struct Object){ .id = 1 };
return obj3_p;
}
Also, as a side note, why do some people cast malloc, and is it better
to do it?
It is considered as bad practice as it silences the warning if there is no prototype of the malloc. Better do not cast. Modern compilers and recent C standard disallow the use of the functions without prototypes
It is better to use sizeof(object) instead of sizeof(type) as if you change the type of the object you need to change oll of the occurrences of the sizeof(type) in your program. It very easy to miss some and get very hard to discover errors.
First, let's straighten out some terminology -
You are declaring all three pointers the exact same way:struct Object* objn_p ...
The only difference is in how you are initializing them.
Declarations in C have two major components - a sequence of declaration specifiers followed by a comma-separated list of optionally initialized declarators. C++ declarations are structured fundamentally the same way, but for this answer I will stick with C terminology (since that's the language we're talking about the one I'm more familiar with).
Declaration specifiers include storage class specifiers (auto, static, register, typedef, etc.), type specifiers (int, char, double, short, etc.), type qualifiers (const, volatile, restrict), and a few others.
Declarators include the name of the thing being declared, along with information about its pointer-ness, array-ness, and/or function-ness.
Initializers for scalar objects are scalars. Initializers for aggregate objects like arrays, structs, and unions are brace-enclosed lists or, in the case of character arrays, a string literal.
In the declaration
struct Object* obj1_p = &obj_1;
the declaration specifier is struct Object, the declarator is * obj1_p, and the initializer is = &obj_1.
I know the C++ convention for declaring pointer objects is T* p, but the syntax is actually T (*p) - the * operator is always bound to the declarator, not the type specifier. If you write T* p, q; then only p is declared as a pointer to T; q is declared as an instance of T. I know why the C++ convention exists, I know the justifications for it, but it does misrepresent how declaration syntax works in both C and C++ and I consider it a mistake to use. Most C programmers will use the T *p convention instead1.
Here are the basic rules for pointer declarations in C:
Declaration Declarator
Specifier
----------- ----------
T *p; // p is a pointer to T
T *a[N]; // a is an array of pointer to T
T *f(); // f is a function returning a pointer to T
T (*a)[N]; // a is a pointer to an array of T
T (*f)(); // f is a pointer to a function returning T
The rules for const are:
T const *p; // p points to a const T
const T *p; // same as above
T * const p; // p is a const pointer to T
What differs between your three methods is how you initialize the pointer.
Method 1 is just taking the address of a previously-declared variable of the same type:
struct Object *obj1_p = &obj1; // using the C convention for pointer declarations
Method 2 is taking the address of a compound literal - basically, an anonymous variable:
struct Object *obj2_p = &(struct Object){ .id = 1 };
The only difference between obj1 and the anonymous object is that you can refer to obj1 directly as well as through the pointer:
printf( "%d %d %d", obj1.id, obj1_p->id, (*obj1_p).id );
whereas you can only refer to the anonymous object through the pointer variable
printf( "%d %d", obj2_p->id, (*obj2_p).id );
Method 3 dynamically allocates memory and assigns the address of the resulting object (which may be NULL if the malloc call fails).
struct Object *obj3_p = malloc( sizeof( struct Object ) );
The chief difference between this and the other two methods is that the memory allocated by malloc hangs around until you explicitly free it, whether the obj3_p variable goes out of scope or not. If obj1 and the anonymous object are declared within a block and without the static keyword, then that memory is automatically released when the block containing them exits.
Also, as a side note, why do some people cast malloc, and is it better to do it?
There are two times when you must cast the result of malloc (and calloc and realloc):
You are compiling the code as C++;
You are working with an ancient, pre-C89 K&R implementation.
Unlike C, C++ does not allow implicit conversion between void * (the type returned from malloc) and other pointer types. You must explicitly cast conversions to or from void *. Having said that, if you're writing C++ you should not be calling malloc directly. You should either be using a container that manages memory for you under the hood (std::string, std::vector, std::map, etc.) or you should be using the new or new [] operators. If you're in the middle of a lift-and-shift from C to C++, it's acceptable to keep the malloc calls until you can get around to rewriting your memory management code, but ideally C++ code should never use malloc (or calloc or realloc) directly.
In the earliest versions of C, malloc, calloc, and realloc returned char *, so you had to cast the result if you were assigning it to pointers of different types:
int *p = (int *) malloc( sizeof *p * N );
As someone who wrote K&R C in college, this was a pain in the ass. It was cluttered and a constant source of mistakes. If you changed the type of p (say from int * to long *) you had to repeat that change in multiple places. That created a higher maintenance burden, especially if (as was often the case) the pointer declaration was separated from the malloc call by other code:
int *p = NULL;
...
p = (int *) malloc( sizeof *p * N );
Prior to C99, you had to declare all variables before any statements (in that block, anyway), so it was common for pointer declarations to be separated from the malloc call by multiple lines of code. It was really easy to change the type of *p in the declaration but forget to do it in the assignment later, causing subtle (and sometimes not-so-subtle) runtime errors.
The 1989 standard introduced the void type and changed the *alloc functions to return void *. It also introduced the rule that you could assign void * values to other pointer types and vice versa without an explicit cast. So you could write a malloc call as:
int *p = malloc( sizeof *p * N );
or
int *p = NULL;
...
p = malloc( sizeof *p * N );
If you change the type of *p, you only have to make that change in one place. It's cleaner, it's harder to screw up, etc.
Also, under the C89 standard, casting the result of malloc could suppress a useful compiler diagnostic if you forgot to include stdlib.h or otherwise didn't have a declaration for malloc in scope. But since C99 did away with implicit int declarations2, that's not really an issue anymore.
However, there are people who prefer to keep the explicit cast for various reasons. Personally, I have found those reasons wanting, and I've accumulated enough scar tissue from bad casts that I prefer to leave them off entirely. 30+ years of writing code has convinced me that when it comes to memory management, simpler is always better. It doesn't hurt to use them; they don't slow down the code or anything like that. But from a readability and maintenance standpoint, casting malloc is bad juju.
Whitespace in declarations is only significant in that it separates tokens. The * character is a token on its own and not part of any identifier, so you can write any of T *p, T* p, T*p, or T * p and they will all be parsed as T (*p).
Prior to C99, if the compiler saw a function call without a preceding declaration, it assumed the function returned int, so if you forgot to include stdlib.h the compiler would complain if you tried to assign the result of malloc to a pointer since an int can't be implicitly converted to a pointer. However, if you used the cast, then the diagnostic would be suppressed.
Related
I am trying to work on an assignment with Struct nodes and linked lists. Here is a small part of the code where I am getting an error that I am unable to resolve. Please help Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct term{
char term[200];
double value;
};
void main(){
struct term **terms = (struct term *)malloc(sizeof(struct term));
// Using **terms is mandatory for the project
*terms[0]->term = "abc";
*terms[0]->value = 1; //This is line 15
//I am unable to fix the error in line 15 at "*terms".
//The error message states "Struct term **terms: Operend of '*' must be a pointer"
//The goal is to create nodes that can be accessed using *terms[i] with a for loop of index i
}
The variable "terms" is a ** (pointer to pointer), but you're dereferencing it three times: once with *, once with [], and once with ->. You probably mean "terms[0]->term".
There are a few problems with this implementation. First of all, you are creating a double pointer so you also need to allocate memory for the inner-pointer you are pointing to.
struct term** terms = (struct term**)malloc(sizeof(struct term*));
*terms = (struct term*)malloc(sizeof(struct term));
Secondly, you cannot do an implementation like shown below.
char c[5];
void main(void)
{
c = "abc";
}
This is wrong and compiler will throw an error. What you can do is to assign each character to the char array one-by-one. Alternatively you can use strcpy too.
((*terms)->term)[0] = 'a';
((*terms)->term)[1] = 'b';
((*terms)->term)[2] = 'c';
Given this ...
struct term **terms = (struct term *)malloc(sizeof(struct term));
... terms represents an object of type struct term **, a pointer to a pointer to a struct term. In that case,
*terms[0]->term is equivalent to *((terms[0])->term) by operator precedence
terms[0] is equivalent to *terms. It is a pointer of type struct term *.
terms[0]->term designates the member term of the structure to which terms[0] points, which is an array. This array value is automatically converted to a pointer to the first array element for the purposes of evaluating the larger expression.
*terms[0]->term therefore represents the first character in the array terms[0]->term. It is equivalent to terms[0]->term[0]. It does not make sense to attempt to assign a string literal to this one character.
Similarly, terms[0]->value designates member value of the structure to which terms[0] points. This is not a pointer, so it is not a suitable operand for the * operator. This is what your compiler's error message is telling you.
Syntactically, any of these would be consistent with the type of terms:
terms[0]->value = 1;
(*terms[0]).value = 1;
(*terms)->value = 1;
HOWEVER, even if one of those were used, the code would still be badly broken. I already mentioned the bad assignment to *terms[0].term. Also, the malloc() call reserves the wrong amount of memory for terms to point to, albeit probably more than enough for a single struct term *. Most problematically, however, terms[0] itself, the pointer, is never initialized with a valid (pointer) value. It doesn't point to anything to whose members you could actually assign. Probably, then, you want something more like this:
struct term **terms = malloc(sizeof(*terms)); // enough space for one pointer
*terms = malloc(sizeof **terms)); // enough space for one struct term
strcpy(terms[0]->term, "abc");
terms[0]->value = 1;
// ...
Plus, the return type of main() must be int, not void.
I am taking the operating system class in mit online, I completed the first assignement http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2012/assignments/MIT6_828F12_assignment1.pdfbut what surprised me is how they return the data structures, they work with a data structure and they return a smaller data structure, and to use it they just cast it back. I see that it can optimize the code but is this safe ? is it good practice ?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct small
{
int n ;
};
struct big
{
int n ;
char * string ;
};
struct small* f()
{
struct big* x = malloc(sizeof(struct big));
x->n = 'X';
x->string = strdup("Nasty!");
return (struct small*) x ;
}
int main(int argc, char *argv[])
{
struct big *y = (struct big*)f();
printf("%s\n",y->string);
}
EDIT 1 : here is the link from mit, i just replicated the idea in my own code.
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2012/assignments/sh.c
No structure is returned, but a pointer to a structure. A pointer contains the address of the memory where the actual object is located. (In this case it has been allocated dynamically with malloc, hence has dynamic storage duration and will live until the pogram ends or a free is called on the pointer.)
The code is legal and has defined semantics. 6.3.2.3/7 of the n1570 draft says
A pointer to an object type may be converted to a pointer to a
different object type. If the resulting pointer is not correctly
aligned for the referenced type, the behavior is undefined. [does not apply here. -ps]
Otherwise, when converted back again, the result shall compare equal
to the original pointer.
Note that the structures could be completely unrelated (the first data element does not need to be the same type). The cast could even be to a built-in type like int.
The issue may be different if the object were accessed through a pointer of the wrong type (which it isn't) because of aliasing issues.
To answer your questions:
Is it safe?
Obviously not. Just casting the type of a pointer to something else is something you should only do if you are positively sure it's the right type.
However, if you know that, everything is fine.
Is it good practice?
No, not in this shape.
You sometimes do Object Oriented Programming in C in a similar way (compare CPython's PyObject): You use one "base" object, and one "type" struct which have a structure like:
struct obj_type {
const char* name;
size_t length; // this is important so that you can copy the object later on without knowing its insides
};
struct obj_base {
obj_type* type;
};
Because it is guaranteed that in C, pointers to structs point to the address of their first element, you can use further objects that build atop of that:
struct more_complex_object {
obj_type* type;
int value;
};
...
int main() {
obj_type *more_complex_object_type = malloc(sizeof(obj_type));
more_complex_object_type->name = "A more complex object type";
more_complex_object_type->length = sizeof(more_complex_object);
more_complex_object *obj = malloc(more_complex_object_type->length);
obj->type = more_complex_object_type;
obj->value = 10;
...
//let's now use it as a simple instance of the "super" object
obj_base* just_an_object = (obj_base*)more_complex_object;
//when we copy things, we make sure to copy the full length:
obj_base* target = malloc(sizeof(more_complex_object));
memcpy(target, just_an_object, just_an_object->type->length);
//now, when handling an object, we can check its type
if(just_an_object->type == more_complex_object_type) {
more_complex_object *my_obj = (more_complex_object)just_an_object;
printf("value: %d\n", my_obj->value);
}
}
Yes it is safe, as long your compiler follows the C standard
Why doesn't GCC optimize structs?
But no, I do not consider this a good practice.
Good practices are relative, and should never be use as a rule. This construction is required if you want to simulate OO inheritance behavior with C.
I have a struct defined like this:
typedef struct
{
int num;
char letter;
}* Foo;
And an array like this:
Foo* items = malloc(sizeof(Foo) * 4);
From my understanding and from the (accepted) answer to this question Dynamic array of pointers to structs, I would expect the above line to only reserve the memory for 4 Foo items, but doesn't initialize it - i.e., if I try to access items[i]->num, I should get some kind of error.
Also, in order to insert item into this array, I should do this:
items[0] = malloc(sizeof(*items[0]));
However, I did a little test, and seems like the following code prints 1 and a just fine:
Foo* items = malloc(sizeof(Foo) * 2);
items[0]->num = 4;
items[0]->letter = 'a';
printf("items[0] = {num=%d, char=%c}\n", items[0]->num, items[0]->letter);
I'm confused. Is this the expected behavior?
Your initial malloc:
Foo* items = malloc(sizeof(Foo) * 4);
Is creating an array of 4 pointers, since Foo is a pointer type. So your second malloc:
items[0] = malloc(sizeof(*items[0]));
Makes sense, since you're allocating a struct to that pointer.
However, the assignment you're doing leads to undefined behavior because you didn't do the second malloc and therefore no space has been allocated to items[0] yet. C won't prevent you from writing to a memory location you shouldn't be writing to. And once you do that, anything can happen.
One thing that's a bit confusing here is that you used typedef to define a pointer type. That can lead to a lot of confusion since it's not apparent by looking at the type that it's a pointer. And in this case, because of how you defined Foo, you had an extra layer of pointer indirection you probably don't need.
So if you define your struct like this:
typedef struct
{
int num;
char letter;
} Foo;
Then this can be done safely:
Foo* items = malloc(sizeof(Foo) * 2);
items[0].num = 4;
items[0].letter = 'a';
printf("items[0] = {num=%d, char=%c}\n", items[0].num, items[0].letter);
Now the malloc creates an array of structs instead of an array of pointers to structs, so an additional layer of mallocs is no longer necessary.
You have to allocate structs and save its pointer to items's elements if you want to use structs. Otherwise, the item's elements are junk and access to it may cause errors.
The second test may worked fine due to the optimization which removes malloc and pass the values of items[0]->num and items[0]->letter directly to printf.
This is happening because you have reserved enough space for the Foo array and probably one element but it's not the expected behavior, there is no expected behavior in this case because what you do invokes undefined behavior.
This will fail if you add more fields to the struct, because then 2 * sizeof(void *) will not be enough. To test it, try adding 2 pointers to the struct like this1
typedef struct
{
int num;
char letter;
void *pointers[2];
} *Foo;
If you want you can do it right, and this is another reason not to typedef a pointer, this would work
typedef struct
{
int num;
char letter;
void *pointers[2];
} Foo;
Foo *foo_pointer = malloc(N * sizeof(Foo));
/* ^ this would be wrong if `Foo' were a pointer */
if (foo_pointer == NULL)
please_abort_this_do_not_continue_because_malloc_has_failed();
foo_pointer[0].num = 1;
foo_pointer[0].letter = 'a';
1It really annoys me to write this, because typedefing a pointer is never a good idea
I'm working my way through the learn c the hard way book and have run into a few issues on Exercise 19. The author said that ex19 was intended for the learners to get to know the macro in c. I have no problem in understanding the concept of that, but I just don't understand everything else. I can't understand how the object prototype is created.
Especilly,what does the following sentense mean?
Since C puts the Room.proto field first, that means the el pointer is
really only pointing at enough of the block of memory to see a full
Object struct. It has no idea that it's even called proto.
the relevant code is this:
// this seems weird, but we can make a struct of one size,
// then point a different pointer at it to "cast" it
Object *el = calloc(1, size);
*el = proto;
can anyone tell me how on earth malloc/calloc exactly works? As far as i know, it just allocate the required number of memory and return the first address. If so, how can the computer know the data struct of the allocated memory? like in the code, after Room *arena = NEW(Room, "The arena, with the minotaur");,you can do this directly arena->bad_guy = NEW(Monster, "The evil minotaur"); how does the computer know there is a bad_guy??
what on earth is the content of *el after the above two statements(Object *el = calloc(1, size); and *el = proto;)?
Any help will be appreciated!!
the link to the exercise: http://c.learncodethehardway.org/book/ex19.html
calloc has the additional feature that it fills the allocated memory with zero bytes, whereas using the equivalent malloc call would require an additional step if all or some of the allocation needs to be zero initially.
In the code
arena->bad_guy = NEW(Monster, "The evil minotaur");
the compiler knows the layout of the struct because the access is through the arena variable, which is declared as a pointer to Room, which is presumably a typedef of a struct.
For the other part, the guarantee of ordering within structs allows a limited form of inheritance in composite structs, or extended structs.
struct A {
int x;
};
struct B {
int foo;
double baloney;
};
struct B (or a pointer to it) can be cast to a (pointer to a) struct A because they both begin with an int. Of course, if you cast the other way, the struct A must have been originally a struct B or access to the baloney field will be undefined. In other words, struct B essentially begins with a struct A.
This may be easier to see if I rewrite my example like this:
struct A {
int x;
};
struct B {
struct A foo;
double baloney;
};
Now you can get a struct A out of struct B in different ways.
struct A a;
struct B b;
a = b.foo; // regular member variable access
struct A *ap = &a;
struct B *bp = &b;
ap = (struct A *)bp; // cast the pointer
ap = & b.foo; // take a pointer from the member variable
ap = & bp->foo; // take a pointer from the member variable via a pointer
All it does is to alloc 1*size bytes. There's nothing magic with malloc/calloc. He is passing the sizeof(T) to the function through that NEW macro and putting it in Object_new's size parameter. So all the function knows is the size in bytes.
I have the following struct and tried to use malloc to create a new object dynamically. It seems to work now with object_ptr obj_ptr1 = malloc(sizeof(object_ptr)); This only assigns the pointer size of object_ptr to obj_ptr1 ? but how do you assign the size of the original struct _object_t in this case?
typedef struct
{
int struct_id;
void *(*function)(void *);
int status;
} _object_t;
typedef struct _object_t* object_ptr;
/* create a new object */
object_ptr obj_ptr1 = malloc(sizeof(object_ptr));
Typedefs for pointer types are often a bad idea, since they can make it difficult to tell whether a type name refers to a pointer type or not.
Suggestion:
typedef struct { /* ... */ } object_t;
object_t *obj1 = malloc(sizeof *obj1);
And if you want to allocate space for an array of N objects:
object_t *obj1 = malloc(N * sizeof *obj1);
Note that I've removed the leading underscore from the type name. Identifiers starting with underscores are reserved to the implementation; you shouldn't define them in your own code.
The malloc(sizeof *foo) idiom may take a little getting used to, but it means you don't have to specify the type name more than once, which avoids errors. (The *obj1 doesn't actually dereference obj1, since the operand of sizeof is not evaluated.)
In your code:
object_t obj1 = malloc(sizeof(object_t));
since object_t is a pointer type, you're allocating enough memory to hold a pointer object, not a structure object. (Using such similar names object_t and _object_t for the pointer and struct types, respectively, undoubtedly contributed to the confusion.)
The problem is in your malloc line. You must allocate sizeof(_object_t) not sizeof(object_t)