So, I'm a C# guy trying my hand at learning C. As a first (personal) project I am attempting to write a basic coordinate geometry library.
Question: Is it again best C programming practices to allocate memory on the heap behind the scenes instead of letting the programmer who is targeting the library do it?
For example, my 'point' struct & related methods:
point.h
/* A basic point type. */
typedef struct point
{
float x;
float y;
float z;
char *note;
}point;
/* Initializes a basic point type. [Free with free_point method] */
point *create_point(float pos_x, float pos_y, float pos_z, char *_note);
/* Frees a point type. */
void free_point(point *_point);
/* Finds the midpoint between two points. */
point *midpoint(point *pt1, point *pt2);
point.c
#include "point.h"
/* Initializes a basic point type. [Free with free_point method] */
point *create_point(float pos_x, float pos_y, float pos_z, char *_note)
{
point *p;
size_t notelen = strlen(_note);
p = (point*)malloc(sizeof(point));
p->x = pos_x;
p->y = pos_y;
p->z = pos_z;
p->note = (char*)calloc(notelen + 1, sizeof(char));
strcpy_s(p->note, notelen + 1, _note);
return p;
}
/* Frees a point type. */
void free_point(point *_point)
{
free (_point->note);
free (_point);
}
/* Creates a midpoint between two points. */
point *midpoint(point *pt1, point *pt2)
{
float mid_x = (pt1->x + pt2->x) * 0.5f;
float mid_y = (pt1->y + pt2->y) * 0.5f;
float mid_z = (pt1->z + pt2->z) * 0.5f;
point *p = create_point(mid_x, mid_y, mid_z, "Midpoint");
return p;
}
Notice that I creating the struct 'point' on the heap FOR whoever implements/uses my lib (in all honesty, this project is just for me and for learning, nevertheless...) via the create_point() method. Is this poor practice? It feels like I am forcing the user into programming a certain way. Same goes with the midpoint() method. Again, you must use pointers to a 'point' struct.
I was unable to find exact questions regarding C library design on SO, but please point me in the right direction if applicable.
Thanks.
It's preference, really. I usually subscribe to letting the user allocate the memory for the object however they wish, and then initializing the members for them.
/* here a non-zero return value might indicate if for example
* we failed to allocate memory for note */
int point_init(struct point* p, int x, int y, char* note)
{
/* ... */
}
/* usage: */
struct point p;
if (point_init(&p, 1, 2, "hello")) {
/* error */
}
This gives the user the option to do things like allocate an array of points that are adjacent in memory, instead of keeping track of an array of pointers to points:
struct point mypoints[NUM_POINTS];
for(size_t i = 0; i < NUM_POINTS; ++i) {
point_init(&mypoints[i], ...);
}
If you want to use the Opaque Pointer strategy to hide the members of your structure from the library user, you will need to allocate the memory for them using the method you described. That's certainly a huge benefit for allocating the structure in your library.
Best practice is to code the library in such a way that the programmer can do what's fastest, since that is a forte of C. For example, the programmer might know that tens of thousands of these point structs will be needed and for the sake of keeping future accesses local might prefer to allocate all the structs in one block of memory. If your library lets the programmer pass pointers in for structs to be initialized, then he can do this. But if you insist on allocating the memory for him, he can't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With