55

Given:

struct objStruct {
    int id;
    int value;
};

typedef struct objStruct Object;

Is there a shortcut to allocate and initialize the object, something like a C++ constructor?
It could even be a preprocessor macro. Whatever makes the code shorter and more readable than this:

Object *newObj = malloc(sizeof(Object));
// successful allocation test snipped
newObj->id = id++;
newObj->value = myValue;
2

6 Answers 6

62

In C I typically create a function in the style of a constructor which does this. For example (error checking omitted for brevity)

Object* Object_new(int id, int value) { 
  Object* p = malloc(sizeof(Object));
  p->id = id;
  p->value = value;
  return p;
}

...
Object* p1 = Object_new(id++, myValue);
Sign up to request clarification or add additional context in comments.

7 Comments

It may be a bit overkill, but I usually also create a void objStruct_destroy(objStruct * obj) that does free for me, in case I need to add any freeing for allocated structs inside later.
Yes; except that it's sometimes preferable to have the "constructor" take the Object* as a parameter instead, so you can do stack allocation as well (Object object; Object_initialise(&object);).
An intermediate method would be have an Object_Init to perform initialization on an already allocated object (=placement constructor), and Object_New to allocate and init (internally it should call Object_Init). The same should be done for the "destructor".
From the perspective of a beginner: Is there a reason why you are giving back the pointer and not the object/struct itself?
@Traubenfuchs because it would mean making a copy at the point of return. It could be elided, but that's not guaranteed.
|
38

In C99 and beyond, you can use a compound literal, which looks like a cast followed by an initializer in braces:

int init_value = ...;
int init_id    = ...;
Object newObj1 = (Object){ .value = init_value, .id = init_id };
Object newObj2 = (Object){ .id = init_id, .value = init_value };

The latter two lines achieve the same effect - the order of the fields is not critical. That is using 'designated initializers', another C99 feature. You can create a compound literal without using designated initializers.

Comments

12

In C it is possible to declare an inline function with the same name as structure:

struct my
{
    int a;
};

inline struct my* my(int* a)
{
    return (struct my*)(a);
}

//somewhere in code
int num = 123;
struct my *sample = my(&num);
//somewhere in code

It looks pretty similar to C++ ctors.

3 Comments

How does return (struct my*)(a) setup the int a? Shouldn't it be assigned by pointer_to_my->a?
That's just a simulation of ctor: the address of num is interpreted as a pointer to a struct which consists of one field - integer. This trick will not work if one adds another field into the struct, e.g. integer or, say, a pointer: passing address into function my() will lead to a garbage in extra field.
ok, but from a C file I get Undefined symbol: _my; no error if I remove the inline keyword
4
struct thingy {
   char * label;
   int x;
};

#define declare_thingy( name, label, val) struct thingy name = { label, val }

struct thingy * new_thingy(const char * label, int val) {
     struct thingy * p = malloc(sizeof(struct thingy));
     if (p) {
          p->label = label;
          p->val = val;
     }
     return p;
}

Comments

2

You really have to distinguish initialization of static or auto variables and dynamic allocation on the head. For the first, do named initializers, for the second a well specified init function.

All that can be nicely packed into macros do give you an easy static/auto intialization and something similar to new in C++.

Comments

1

If you are looking for an object oriented "emulation" over C, I strongly recommend the GObject Type System [1], it's mature and largely used by GTK for instance.

GLib [2] has also a nice slice allocator for small objects, currently used by GNOME.

[1] GObject Reference Manual

[2] GLib Memory Slices

2 Comments

Be aware though that some things which are normally checked at compile time in C++, are done only at run time in GObject.
Wanted to add, that although GObject is a decent library for object oriented programming, it comes with a bit of overhead. Namely atomic reference counting. Which is not required in most cases, unless the objects are shared between threads. Also, because of the external GObject calls, for example creation of a new object can't be optimized. (The compiler can't inline an optimized initializer). Furthermore the Slice Allocator has been removed and replaced with malloc since GLib 2.76.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.