Although there are no classes in C you would want to have something like private variables in C. Encapsulation and hiding implementation details can be useful, so how can this be done in the procedural C language?
There are no Private Variables in C. One can emulate private variables by using the keyword static within a .c file and declaring appropriate get and set functions (methods) in the header. The definition for this should then be implemented in the c. file.
The implementation of this is not very complicated, so let’s move on to an example.
Implementing Private Variables with Getters and Setters in C
In the C programming language there is no keyword private. There are no classes and in a struct everything is public all the time. There is however the keyword static.
When used within a function, static makes the variable save it’s content at a fixed memory address. When called the first time, the initialization value is set. When called again, the last saved value is used again.
The second usage of static is the one we are after. When a variable (or function) is declared globally within a .c-File and given the keyword static, then the access to it is limited to the file. This way we can declare a private variable and/or private function.
...
static int privateNumber; /* Only accessible in example.c */
/* Only accessible in example.c */
static void privateFunction()
{
static int permanentNumber = 10; /* Initialized with 10 */
permanentNumber++; /* 11 on second call, 12 on third call, etc. */
}
...
Private Variables with Getters/Setters in C – Simple Approach
We simply write a rectangle.h file with all the getters and setters that we need, but we do not declare the variables yet:
#pragma once
/* Getters */
int get_x();
int get_y();
int get_width();
int get_height();
/* Setters */
void set_x(const int x);
void set_y(const int y);
void set_width(const int width);
void set_height(const int height);
/* Other Functions... */
void draw();
Then, in out .c-File we define all those functions and we also declare the variables. We use the keyword static to keep them only accessible in this file and so they are private to the rectangle:
#include "rectangle.h"
/* Using static within a c-File makes the
Variables inaccessable from the outside
and therefore private */
static int _x;
static int _y;
static int _width;
static int _height;
/* Getters */
int get_x() { return _x; }
int get_y() { return _y; }
int get_width() { return _width; }
int get_height() { return _height; }
/* Setters */
void set_x(const int x) { _x = x; }
void set_y(const int y) { _y = y; }
void set_width(const int width) { _width = width; }
void set_height(const int height) { _height = height; }
/* Other Functions...*/
void draw()
{
/* Draw the Rectangle */
}
In a program, we can then access it like this:
#include <stdio.h>
#include "rectangle.h"
int main()
{
set_x(200);
printf("X is %d", get_x());
return 0;
}
Private Variables with Getters/Setters in C – Advanced Approach
The simple approach works well but it has one major downside: You can only have one rectangle in the whole application. If you want to have more than one rectangle you will have to take a different approach.
I explained how you can emulate classes in C in another article. With this approach you can use static structs and pointers to implement private variables and be able to construct as many rectangles as you want (or as many as your memory can handle).
So our rectangle might now look like this:
#pragma once
struct rectangle_t; /* Declaration for the Compiler */
struct rectangle_t* rectangle_new(); /* Creation Function */
void rectangle_ctr(struct rectangle_t* obj, const int x, const int y,
const int width, const int height); /* Constructor */
void rectangle_dtr(struct rectangle_t* obj); /* Destructor */
/* Getters */
int rectangle_get_x(struct rectangle_t* obj);
int rectangle_get_y(struct rectangle_t* obj);
int rectangle_get_width(struct rectangle_t* obj);
int rectangle_get_height(struct rectangle_t* obj);
/* Setters */
void rectangle_set_x(struct rectangle_t* obj, const int x);
void rectangle_set_y(struct rectangle_t* obj, const int y);
void rectangle_set_width(struct rectangle_t* obj, const int width);
void rectangle_set_height(struct rectangle_t* obj, const int height);
/* Other Functions...*/
void rectangle_draw();
We only declared the struct person_t so the outside world does not know it’s members. In order to create an rectangle object you have to allocate the memory which is the purpose of the rectangle_new() function. Then you have an object pointer which identifies the instance you just created.
In the .c-File you then define the struct (for which no static is needed) and put all the private variables in there. Now you can define all the member functions, using the incoming object pointer as replacement for a this pointer you would have in a class.
There also is a constructor to initialize the member variables and a destructor to free all the dynamically allocated memory. The downside is that you have to be very disciplined to call these functions in the code or you will get memory leaks and other nasty effects.
The implementation then looks somewhat like this:
#include "rectangle_class.h"
#include <stdlib.h>
#include <string.h>
struct rectangle_t {
int x;
int y;
int width;
int height;
};
struct rectangle_t* rectangle_new()
{
return (struct rectangle_t*)malloc(sizeof(struct rectangle_t));
}
void rectangle_ctr(struct rectangle_t* obj, const int x, const int y,
const int width, const int height)
{
obj->x = x;
obj->y = y;
obj->width = width;
obj->height = height;
}
void rectangle_dtr(struct rectangle_t* obj)
{
/* Free all allocated memory */
free(obj);
}
/* Getters */
int rectangle_get_x(struct rectangle_t* obj)
{
return obj->x;
}
int rectangle_get_y(struct rectangle_t* obj)
{
return obj->y;
}
int rectangle_get_width(struct rectangle_t* obj)
{
return obj->width;
}
int rectangle_get_height(struct rectangle_t* obj)
{
return obj->height;
}
/* Setters */
void rectangle_set_x(struct rectangle_t* obj, const int x)
{
obj->x = x;
}
void rectangle_set_y(struct rectangle_t* obj, const int y)
{
obj->y = y;
}
void rectangle_set_width(struct rectangle_t* obj, const int width)
{
obj->width = width;
}
void rectangle_set_height(struct rectangle_t* obj, const int height)
{
obj->height = height;
}
/* Other Functions...*/
void rectangle_draw(struct rectangle_t* obj)
{
/* Draw the Rectangle */
}
And then we can finally use this class-like rectangle to create as many rectangles in our program as we want (in this case, only two):
#include <stdio.h>
#include "rectangle_class.h"
int main()
{
/* Allocate Memory */
struct rectangle_t* rect1 = rectangle_new();
struct rectangle_t* rect2 = rectangle_new();
/* Initialize Rectangles */
rectangle_ctr(rect1, 20, 20, 150, 300);
rectangle_ctr(rect2, 10, 15, 200, 400);
/* Do Stuff */
rectangle_set_x(rect1, 15);
rectangle_set_y(rect2, 25);
printf("Rect1 X is %d\n", rectangle_get_x(rect1));
printf("Rect2 X is %d\n", rectangle_get_x(rect2));
printf("Rect1 Y is %d\n", rectangle_get_y(rect1));
printf("Rect2 Y is %d\n", rectangle_get_y(rect2));
rectangle_draw(rect1);
/* Free Memory */
rectangle_dtr(rect1);
rectangle_dtr(rect2);
return 0;
}
These are two ways of dealing with private variables in C. You can take it a bit further with Inheritance and Polymorphism which is described in this article.