Header Banner
wonderhowto.mark.png
Gadget Hacks Next Reality Food Hacks Null Byte The Secret Yumiverse Invisiverse Macgyverisms Mind Hacks Mad Science Lock Picking Driverless
Home
CodeByte

How to Write Constructor and Destructor Functions in C

Feb 12, 2016 12:03 AM
Feb 12, 2016 12:37 AM

Hey everyone.

Only found the site a few days ago but really loving it. Been

reading and learning new things and always grateful for that.

I used to code in c, as well as a few other languages a few years

back but fell away from it a bit due to various other commitments.

Getting back into the swing of it after a long break and having

some fun.

Anyways - I'm playing about with something right now and it dawned

on me that it's something I used more often than I realised. Sort

of took it for granted but it's quite useful so I thought I'd share

and hope someone gets something useful out of it.

Constructors and destructors in c.

I used to find that my programs became sloppy and quite messy rather

quickly.

It's easy enough to write more maintainable code, but my main() in

particular would end up cluttered. It more often than not became a

repugnant mess of initialisations, assignments, argument processing

and all that.

Then there's the middle bit where the program does what it's actually

supposed to...then the sloppy, messy cleanup at the end.

Another problem I found was that on the odd occasion my code would

exit without cleaning up...closing files, free()'ing memory. Normally

this is a result of poor logic or design, but it becomes a pain

having to make sure you tidy up everything nice at every point

in your code where your program might exit.

When I discovered how to use constructor and destructor functions in

c it made things a lot cleaner. My main() functions became more

streamlined - I began to care less about initialising because I'd

have a constructor to take care of it for me, and cleanup was pretty

easy since I had a destructor to do the work when my program exited.

Declaring the functions.

Your prototypes can declare the constructor and destructor functions

using _attribute_

Have a look https://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/Function-Attributes.html for more info - you can use _attribute_ for a few different things.

void function_ctor (void) __attribute__ ((constructor));

void function_dtor (void) __attribute__ ((destructor));

These are the prototypes for both types, we can be sure that the

constructor will be called before main() begins, and immediately

after main returns or the program exits.

Try it:

#include <stdio.h>

#include <stdlib.h>

void function_ctor (void) __attribute__ ((constructor));

void function_dtor (void) __attribute__ ((destructor));

int

main(void)

{

return 0;

}

void

function_ctor(void)

{

fprintf(stdout, "Constructor.\n");

}

void

function_dtor(void)

{

fprintf(stdout, "Destructor.\n");

}

Run the program, it's simple enough and quite self-explanitory,

most people familiar enough with c will know about this, newcomers

may not, but the usefulness of this feature is quite obvious to

even the freshest of coders.

Another example.

Here's a more useful example - we will have two global FILE *

streams, one for standard output and one for standard error.

Our constructor will assign them default values - namely stdout

and stderr, respectively. These are standard c FILE * streams, we always have stdin (0), stdout (1) and stderr (2).

A simple function will process command line arguments which can

be used to specify alternative output files.

Our destructor will then close non-standard FILE streams so that

any writing done to either stream will be saved.

The code is well commented and simple enough to understand, here

it is:

/**

* fattrs.c

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

/**

* Some naughty globals....oh dear!

*/

FILE *_o_stream;

FILE *_e_stream;

/**

* We need a constructor that will assign our globals so that

* we can use them safely in our program...

*

* These are just the prototypes - the actual functions appear

* below main().

*/

void __init_streams (void) __attribute__ ((constructor));

void __close_streams (void) __attribute__ ((destructor));

/**

* Apparently, it ain't a good idea to do too much work directly

* in your constructor/destructor functions.

*

* So I've heard, now I ain't gonna tell no lies - I dunno why,

* maybe it's about keeping it neat, or maybe there's a proper

* good reason for this...personally, I've done loads of things

* directly within my constructor/destructor functions in the

* past without any problems.

*

* Still, for the sake of neatness, maintainability and just to

* be pedantic we'll instead use the constructor/destructor as

* wrapper functions which will call on the following functions

* to carry out the actual work...

*

* Note that these function names begin with a single _ whereas

* the constructor/destructors begin with __

*/

void _init_streams (void);

void _close_streams (void);

/**

* For processing command line args...

*/

void args (int, char **);

int

main(

int argc,

char **argv

)

{

/**

* The args() function will process the command line arguments...

*/

args(argc, argv);

/**

* Since the constructor is called before main() executes, we

* can be sure that the global FILE streams have been initialised

* to point at standard output streams (stdout and stderr).

*/

fprintf(_o_stream, "This is the output stream.\n");

fprintf(_e_stream, "This is the error stream.\n");

}

void

__init_streams(void)

{

_init_streams();

}

void

__close_streams(void)

{

_close_streams();

}

void

_init_streams(void)

{

/**

* We'll just assign standard FILE streams so that we can

* write to stdout and stderr....

*/

_o_stream = stdout;

_e_stream = stderr;

}

void

_close_streams(void)

{

/**

* We don't need to bother closing non-standard FILE streams,

* although the args() function may have opened specific

* files for output, in which case we need to close them.

*/

if (_o_stream != stdout)

fclose(_o_stream);

if (_e_stream != stderr)

fclose(_e_stream);

}

/**

* This is all pretty self-explanitory.

*/

void

args(

int argc,

char **argv

)

{

int a;

for (a = 1; a < argc; a++) {

/**

* The -o option is used to specify an output file,

* -e to specify an error file.

*/

if (strcmp(argva, "-o") == 0) {

if (++a >= argc) {

fprintf(estream, "Usage: %s -o <file>\n", argv0);

exit(EXIT_FAILURE);

}

_o_stream = fopen(argva, "w");

if (_o_stream == NULL) {

fprintf(_e_stream, "Error opening output file %s!\n", argva);

exit(EXIT_FAILURE);

}

continue;

}

else if (strcmp(argva, "-e") == 0) {

if (++a >= argc) {

fprintf(_e_stream, "Usage: %s -e <file>\n", argv0);

exit(EXIT_FAILURE);

}

_e_stream = fopen(argva, "w");

if (_e_stream == NULL) {

/**

* We don't have an error stream to write to!

*

* We'll have to default to stderr...

*/

fprintf(stderr, "Error opening error file %s!\n", argva);

exit(EXIT_FAILURE);

}

continue;

}

else {

fprintf(_e_stream, "%s: Unknown option!\n", argva);

exit(EXIT_FAILURE);

}

} // End of for()

} // End of args()

/* End of file. *./

Compile it:

# gcc fattrs.c -o fattrs

By default, the FILE streams are assigned stdout and stderr, we

can run the program and we'll get the following:

# ./fattrs

This is the output stream.

This is the error stream.

We can redirect stdout and stderr to separate files, like this

# ./fattrs 1> out 2> err

# cat out

This the output stream.

# car err

This is the error stream.

We redirect the output of 1 (stdout) to the file named 'out' and

the output of 2 (stderr) to the file named 'err'.

If we want to change the output streams to write directly to

these files instead - first delete out and err:

# rm out err

Run attrs again, this time we'll use the -o and -e options to

specify paths to the output files:

# ./fattrs -o out -e err

You can cat the 'out' and 'err' files to verify that they were

indeed written.

Lastly.

I'd recommend you write a simple program that has 3 constructors

and 3 destructors.

Call them ctor_1(), ctor_2(), ctor_3(), dtor_1(), dtor_2() and

dtor_3().

Have each of them print a string, for example have ctor_1() dump

"This is constructor 1." to stdout, dtor_1() print "This is

destructor 1." to stdout, and so on.

Note the order they are executed in - they are automatically

prioritised based on the order of declaration.

We can use something like this to set the priority level ourselves:

((constructor (101)))

((constructor (102)))

And so on, play about with it and have fun.

Happy coding!

The next big software update for iPhone is coming sometime in April and will include a Food section in Apple News+, an easy-to-miss new Ambient Music app, Priority Notifications thanks to Apple Intelligence, and updates to apps like Mail, Photos, Podcasts, and Safari. See what else is coming to your iPhone with the iOS 18.4 update.

Related Articles

Comments

No Comments Exist

Be the first, drop a comment!