2

Let's say for example that I have defined a struct emp_t. Then, I want all employees (objects) created from that structure to have ID = 0, name = "Empty string", and salary = 0. Is this possible?

struct employee
{
    int id;
    char name [30];
    int salary;
};
typedef struct employee emp_t;
Omar Ahmed
  • 33
  • 4
  • 4
    Use a function to create and initialize the structure? – Some programmer dude Jul 16 '20 at 19:09
  • 1
    C doesn't have constructors, you need to define your own initialization function a use it. – Jack Jul 16 '20 at 19:10
  • 3
    Use `calloc` instead of `malloc` maybe? `emp_t *person1 = calloc(1, sizeof(emp_t));` – ssd Jul 16 '20 at 19:11
  • 1
    This might be answered here: https://stackoverflow.com/questions/330793/how-to-initialize-a-struct-in-accordance-with-c-programming-language-standards – Hellmar Becker Jul 16 '20 at 19:25
  • @ssd, just because something is a `struct`, there is no automatic need to put it in the heap. Actually that is one of the major advantages `c` and `c++` has over other higher level languages. – HAL9000 Jul 16 '20 at 20:29
  • @ssd: And if the employee is on the stack? – einpoklum Jul 16 '20 at 20:29
  • @einpoklum One way to ensure initialization is to mimic the PIMPL idiom in C, remembering that member functions are conceptually ordinary functions with a `this` parameter -- just that `this` is hidden in C++. So the user sees only ever incomplete types and functions operating on pointers to objects, and can create objects only through factories. Of course you lose speed but who except the flashboys really needs that? – Peter - Reinstate Monica Jul 16 '20 at 20:35
  • @HAL9000 : I'm aware, that was not a definitive solution but just a short trick which will work in case allocating memory from heap wouldn't matter too much. – ssd Jul 16 '20 at 21:08
  • @einpoklum : I have no solution then, other than creating a distinct function responsible for zeroing out all the fields one by one. – ssd Jul 16 '20 at 21:12
  • It’s meaningless for an array to “be `NULL`”. The answers here assume you meant (all 0 (NUL, in ASCII) bytes). – Davis Herring Jul 18 '20 at 21:32

3 Answers3

3

One sensible way of doing it is via function. When you have you employee structure, you can do:

void employee_init(struct employee* p)
{
    p->id      = 0;
    p->name[0] = '\0';
    p->salary  = 2000;
}

You can also use a simple macro:

#include <stdio.h>

typedef struct {
    int  id;
    char name[30];
    int  salary;
} Employee;

#define EMPLOYEE(var)   \
    Employee var;       \
    var.id      = 0;    \
    var.name[0] = '\0'; \
    var.salary  = 0;

int main()
{
    EMPLOYEE(john);
    printf("%d %s %d\n", john.id, john.name, john.salary);
    return 0;
}

Or combine both:

#define EMPLOYEE(var) \
    Employee var;     \
    employee_init(&var);

When you use malloc():

#define EMPLOYEE_DYN(var, n)  \
    Employee* var = malloc(sizeof(Employee) * n);  \
    employee_init(var);

Since C99 you can also do:

#define EMPLOYEE(var) \
    Employee var = { .id = 0, .name = "", .salary = 0 }

More constructor-like macro (in this case #include <string.h> will be needed):

#define EMPLOYEE(var, id_, name_, salary_) \
    Employee var;                          \
    var.id = id_;                          \
    strcpy(var.name, name_);               \
    var.salary = salary_;

Or with designated initializer:

#define EMPLOYEE(var, id_, name_, salary_) \
    Employee var = { .id = id_, .name = name_, .salary = salary_ }
Jorengarenar
  • 2,705
  • 5
  • 23
  • 60
  • This is a really very good answer. Just a small modification in the way of using function-like macro, notice that writing `strcpy(var.name, "John");` would force all created object to initially have the name John, so instead we can use `var.name [0] = '\0';` because that way you have the first character as the null terminator; hence it is an empty string. Other than that, your answer was really helpful! – Omar Ahmed Jul 16 '20 at 23:19
2

Preliminary note: member name of your structure is an array, not a pointer. NULL is not a valid value for it. The value it would be initialized with by default initialization is all(30)-bytes-zero, which is an entirely different thing.

With that said, you get default initialization by, um, default for objects declared with static storage duration. That is, all those declared at file scope and those declarared at block scope with the keyword static. For those cases you don't need to do anything special.

// at file scope
struct employee emp1;  // done

int do_something() {
    static struct employee emp2;  // done (but any changes persist across calls)
    // ...
    return 0;
}

For those with automatic storage duration -- that is, block-scope variables that are not static -- you need to provide an initializer. It is important here to understand that

  • although braces are required around the initializer for an aggregate, nested braces are optional for the initializers of nested aggregates;
  • the constant 0 can be used to initialize any scalar, and the result is the same as what default initialization would achieve; and
  • if an initializer for an aggregate does not initialize all members then those not explicitly initialized are implicitly default initialized.

What that all means is that you can achieve manual initialization equivalent to default initialization for any object via the initializer {0} (though be careful here with arrays of unspecified length). In particular, this is utterly idiomatic:

int do_something_else() {
    struct employee emp3 = {0};

    // ...

    return 0;
}

Note well that initialization is not assignment! That syntax is not (quite) valid for an assignment statement.

And that brings us around to the last case: allocated objects. These are not subject to default initialization and they cannot have initializers. It is fairly common to set initial values for these by setting them to all bytes zero, either by allocating them with calloc() or by zero-filling them after allocation with memset(). Neither of those approaches supports strictly conforming programs in all cases, however, because pointer and floating-point object representations with all bits zero are not necessarily the same as you get from default initialization of such objects.

You can always just assign appropriate values to each member individually, but in modern C, the cleanest thing to do to set an allocated structure's value as if it had been default initialized is to assign an appropriate a compound literal value to it:

int do_a_third_thing() {
    struct employee *ep = malloc(sizeof(*ep));
    // ... check for / handle NULL ...

    *ep = (struct employee) {0}; // HERE

    // ...

    return 0;
}

In that example, the expression (struct employee) {0} has type struct employee and (initial) value as if it were declared with an initializer of {0}.

Of course, you can do the same thing to assign a new, as-if-default-initialized value to any structure or union object, regardless of its storage duration.


With all that said, the bottom line answer to

I want all employees (objects) created from that structure to have ID = 0, name = NULL, and salary = 0. Is this possible?

Is no, it is not possible per se. C does not have constructors, default or otherwise. As I have shown, however, if default initialization is what a user wants for a particular instance, then it is fairly easy for them to get it. And if it's not what they want, then it is not useful to force that on them.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

Here's another way, using a 'prototype' approach.

# include <stdlib.h>
# include <string.h>
# include <stdio.h>

struct employee  {
    int id;
    char name [30];
    int salary;
};

struct employee const * const   create_employee_prototype()   {
    struct employee * prototype = (struct employee *) malloc( sizeof(struct employee) );
    prototype->id = 0;
    for (int i = 0; i < 30; ++i) { prototype->name[i] = 0; }
    prototype->salary = 0;
    return (struct employee const * const) prototype;
}

struct employee *   generate_new_employee_from_prototype( struct employee const * const prototype )   {
    struct employee * employee_out = malloc( sizeof( struct employee ) );
    employee_out = memcpy( employee_out, prototype, sizeof( struct employee ) );
    return employee_out;
}

int   main()   {
    struct employee const * const PROTOTYPE = create_employee_prototype();
    struct employee * employee1 = generate_new_employee_from_prototype( PROTOTYPE );
    printf( "Employee 1 initialised with id: %d, name: %s, and salary: %d.\n", employee1->id, employee1->name, employee1->salary );
    free( employee1 );
    free( (void *) PROTOTYPE );
}

Effectively create a constant object in memory that you can generate copies of. Disadvantage: generated on the heap in a 'non-visible' way, so you need to know/remember to free the memory manually. (obviously you're free to adapt this to use the stack instead.)

Tasos Papastylianou
  • 21,371
  • 2
  • 28
  • 57